用DP记录贡献的问题

很多时候记录贡献比动态维护能够得到更优的复杂度

Codeforces Round 809 (Div. 2) D2

错解:

很容易观察到每个位置有 O ( n ) O(\sqrt n) O(n )种取值,我们找出这些值,从大到小枚举最小值的同时维护最小的最大值即可,得到了一个 O ( n n l o g n ) O(n\sqrt nlogn) O(nn logn)的解法(常数也很大),但是不足以通过本题。

int n,m,k;
int a[N];
vector<int>nxt[N];
void solve(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)nxt[i].clear();
	set<int>val;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		for(int l=1;l<=k;){
			if(l>a[i]){
				val.insert(0);
				nxt[i].pb(a[i]/l);
				break;
			}
			int r=a[i]/(a[i]/l);
			val.insert(-(a[i]/l));
			nxt[i].pb(a[i]/l);
			l=r+1;
		}
		reverse(nxt[i].begin(),nxt[i].end());
	}
	priority_queue<PII>q;
	int ans=inf,mn=inf;
	for(int i=1;i<=n;i++)q.push(mp(a[i],i)),mn=min(mn,a[i]);
	for(int tx:val){
		int x=-tx;
		if(x>mn)continue;
		int pos=q.top().se;
		while(nxt[pos].size()>1&&nxt[pos][nxt[pos].size()-2]>=x){
			PII t=q.top();q.pop();
			nxt[pos].pop_back();
			q.push(mp(nxt[pos].back(),t.se));
			pos=q.top().se;
		}
		ans=min(ans,q.top().fi-x);
		if(nxt[pos].size()<=1)break;
	}
	cout<<ans<<'\n';
}
正解

我们利用一个数组 m n _ m x mn\_mx mn_mx来记录贡献, m n _ m x [ i ] mn\_mx[i] mn_mx[i]表示当最小值为 i i i时,最大值最小为多少,我们在计算每个位置可能的取值时把它处理出来即可,得到了 O ( n n ) O(n\sqrt n) O(nn )的解法,可以通过本题。

int n,m,k;
int a[N],mn_mx[N],num[N];
void solve(){
	cin>>n>>k;
	unordered_set<int>del;
	for(int i=1;i<=n;i++)cin>>a[i];

	for(int i=1;i<=n;i++){
		int pre=inf;
		for(int l=1;l<=k&&l<=a[i];){
			int r=a[i]/(a[i]/l);
			int val=a[i]/l;
			num[val]=1;
			mn_mx[val+1]=max(mn_mx[val+1],pre);
			del.insert(val),del.insert(val+1);
			
			pre=val;
			l=r+1;
		}
		mn_mx[0]=max(mn_mx[0],pre);
	}
	int ans=inf,up=mn_mx[0];
	for(int i=0;i<=a[1];i++){
		up=max(up,mn_mx[i]);
		if(num[i])ans=min(ans,up-i);
	}
	
	for(int x:del)num[x]=mn_mx[x]=0;
	num[0]=mn_mx[0]=0;
	
	cout<<ans<<'\n';
}

Educational Codeforces Round 125 (Rated for Div. 2) D

错解

可以观察到对于第 i i i m o n s t e r monster monster,我们要求的就是 m i n 1 ≤ j ≤ n ( H i D i h j d j + 1 ) ∗ c j \mathop{min} \limits_{1\leq j\leq n} {(\frac{H_iD_i}{h_jd_j}+1)*c_j} 1jnmin(hjdjHiDi+1)cj,观察到 H i D i h j d j \frac{H_iD_i}{h_jd_j} hjdjHiDi最多有 O ( H i D i ) O(\sqrt{H_iD_i}) O(HiDi )种取值,我们将不同的 u n i t unit unit h i d i h_id_i hidi从小到大排序,再用 S T ST ST表处理出区间最小值,可以得到一个 O ( m H D / C l o g n + n l o g n ) O(m\sqrt{HD/C}logn+nlogn) O(mHD/C logn+nlogn)的方法,同样不足以通过此题。

int n,m,k;
int a[N],b[N],tot=0;
int C;
int ST[N][21],lg2[N];
void init_ST(int* num,int n){
	for(int i=1;i<=n;i++)ST[i][0]=num[i];
	for(int j=1;j<=20;j++){
		for(int i=1;i+(1<<j)-1<=n;i++){
			ST[i][j]=min(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
		}
	}
}
int query(int l,int r){
	int mid=lg2[r-l+1];
	return min(ST[l][mid],ST[r-(1<<mid)+1][mid]);
}
int find(int s,int x){
	int l=s,r=tot;
	if(b[r]<=x)return r;
	while(l+1<r){
		int mid=l+r>>1;
		if(b[mid]<=x)l=mid;
		else r=mid;
	}
	return l;
}
inline void solve(){
	cin>>n>>C;
	lg2[0]=-1;
	for(int i=1;i<=n;i++)lg2[i]=lg2[i>>1]+1;
	map<int,int>num;
	for(int i=0;i<n;i++){
		int c,d,h;cin>>c>>d>>h;
		if(num.count(d*h))num[d*h]=min(num[d*h],c);
		else num[d*h]=c;
	}
	tot=0;
	for(auto t:num){
		a[++tot]=t.se;
		b[tot]=t.fi;
	}
	init_ST(a,tot);
	
	cin>>m;
	while(m--){
		int D,H;cin>>D>>H;
		int ans=INF,mul=D*H;
		for(int i=find(1,mul/C);i<=tot;i++){
			int t=mul/b[i],vr;
			if(t)vr=mul/t;
			else vr=INF;
			int pr=find(i,vr);
			
			int mn=query(i,pr);
			ans=min(ans,(t+1)*mn);
			i=pr;
		}
		if(ans>C)cout<<-1<<' ';
		else cout<<ans<<' ';
	}
}
正解

我们还观察到,使用 k k k u n i t unit unit可以击败 1 ≤ H D ≤ k ∗ h d − 1 1\leq HD\leq k*hd-1 1HDkhd1内的所有怪物,于是同样的,我们利用数组 d p dp dp来计算贡献, d p [ i ] dp[i] dp[i]表示花费为 i i i时,可以打败 H D HD HD最大为多少的怪物,显然它是单调不降的,处理之后在 d p dp dp数组上二分即可,复杂度为 O ( ( n + m ) l o g C ) O((n+m)logC) O((n+m)logC)

int n,m,k;
int dp[N];
int C;
inline void solve(){
	cin>>n>>C;
	map<int,int>num;
	for(int i=0;i<n;i++){
		int c,d,h;cin>>c>>d>>h;
		if(num.count(c))num[c]=max(num[c],d*h);
		else num[c]=d*h;
	}
	for(auto t:num){
		for(int j=t.fi;j<=C;j+=t.fi){
			int cnt=j/t.fi;
			dp[j]=max(dp[j],cnt*num[t.fi]-1);
		}
	}
	
	for(int i=1;i<=C;i++)dp[i]=max(dp[i],dp[i-1]);
	
	cin>>m;
	while(m--){
		int H,D;cin>>H>>D;
		int l=0,r=C;
		if(dp[r]<H*D){
			cout<<-1<<' ';
			continue;
		}
		while(l+1<r){
			int mid=l+r>>1;
			if(dp[mid]<H*D)l=mid;
			else r=mid;
		}
		cout<<r<<' ';
	}
}

你可能感兴趣的:(算法,数据结构)