CF1322D Reality Show【状压DP】

CF1322D Reality Show

Description

(n) 个元素,每个元素的大小为 (l_i) ,选择这个选手花费 (s_i) 的代价,大小为 (i) 的元素对应 (c_i) 的收益。依次将 (n) 个选手加入集合中,得到权值如下:

  • 加入元素 (i) 时,立刻获得收益 (c_{l_i});
  • 如果此时场上有大小相同的元素,将它们合并为一个大小等于这两个元素大小 (+1) 的数,获得新数对应的收益。重复此操作直到没有大小相同的元素。

求一个 (l_i) 单调不升的元素序列,将它们依次加入集合中,最大化其收益减去代价的值 (一个也不选也是合法的)。

(n,mle 2000,s_ile 5000,|c_i|le 5000)

Solution

考虑朴素 (DP),定义 (dp_{i,s}) 表示上一个选择的元素的大小为 (i),目前集合中的元素为 (s) 时的最大收益,然后暴力枚举新加入的数的贡献来判断。

状态数实在太大了,考虑新加入的数加入后对状态的影响,可以发现它的贡献要么是连续合并许多个 (1) 直到遇到 (0),要么就只会影响最后 (log n) 个数。前者的贡献难以计算,但我们可以将整个过程倒过来,在倒着的元素序列上找一个单调不减的元素序列,加入集合中,那么此时不会影响总答案,且不会再产生前一种贡献。

这是我们就只用维护最后 (log n) 的数的状态即可,总状态数为 (mathcal O(2^{log n})=mathcal O(n))。转移时,考虑当前元素的贡献可以 (mathcal O(log n)) 暴力判断。因此总复杂度为 (mathcal O(n^2log n))

Code

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=2060;
int n,m,l[N],a[N],c[N<<1],dp[N][N],sum[N<<1],mx[N],trans[N][12];
int f[N][N],sf[N][N],k;

struct BIT{
	int c[N];
	inline int lowbit(int x){return x&(-x);}
	inline void init(){memset(c+1,-0x3f,sizeof(int)*(m+1));}
	inline void add(int x,int v){
		x++;
		for(;x<=m+1;x+=lowbit(x)) c[x]=max(c[x],v);	
	}
	inline int query(int x){
		x++;
		int ret=-inf;
		for(;x;x-=lowbit(x)) ret=max(ret,c[x]);
		return ret;
	}
}T[N];

int main(){
//	freopen("test.in","r",stdin);
//	freopen("talent.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&l[i]);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	for(int i=1;i<=n+m;++i) scanf("%d",&c[i]);
	for(int i=1;i<=n+m+1;++i) sum[i]=sum[i-1]+c[i];
	l[n+1]=-n;
	k=1;while((1<<k)<=max(n,m)) ++k;
	const int M=(1<<k)-1;
	memset(dp,-0x3f,sizeof(dp));
	memset(f,-0x3f,sizeof(f));
	dp[n+1][1]=0;
	f[0][1]=0;
	for(int i=0;i<=M;++i) T[i].init();
	T[1].add(0,0);
	int ans=0;
	for(int s=0;s<M;++s){
		for(int i=0;i<=k;++i) if((s>>i)&1){mx[s]=i;break;}
		for(int i=0;i<=k;++i) trans[s][i]=((s>>i)+1)&M;
	}
	for(int i=n;i>=1;--i){
		for(int t=l[i];t>=0;--t){
			bool flag=(t==l[i]-k);if(!t) flag=true;
			int tp=flag?k:l[i]-t;
			for(int s=0;s<=M;++s){
				int tmp=trans[s][tp];
				int ff=(flag?T[s].query(t):f[t][s])-a[i]+sum[l[i]+mx[tmp]]-sum[l[i]-1];
				dp[i][tmp]=max(dp[i][tmp],ff);
			}
			if(flag) break;
		}
		for(int s=0;s<=M;++s){
			ans=max(ans,dp[i][s]);
			f[l[i]][s]=max(f[l[i]][s],dp[i][s]);
			T[s].add(l[i],dp[i][s]);
		}
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/tqxboomzero/p/14931588.html