uoj110

一开始看成了每次不一定要选一个连续的区间,结果死活不会做。
一个显然的想法:
\(f_{i,j,k}\)表示成功划分前\(i\)个元素,划分了\(j\)段,\(xor\)和为\(k\)是否可行。
根据xor的封闭性,在前3个sub内,\(k\)只需要开到2048。
然而后面的数据\(a\)非常大。
考虑按位贪心。
对于个数限制为一个区间的sub,钦定一个前缀,表示所有分出来的段都要为这个前缀。
可以使用dp判定。设\(g_{i,j}\)表示成功划分前\(i\)个元素,划分了\(j\)段是否可行。
然后可以简单转移。
对于个数限制为\(\leq v\)的sub,前面的方法会超时。
但是并不用记录后一维。
实际上,我们让分出的段数尽可能小即可。
可以使用dp判定。设\(g_{i}\)表示成功划分前\(i\)个元素,至少划分成多少段。
按照定义转移。
最终复杂度\(O(n^3\log_2{suma_i})\)(第四个sub)或者\(O(n^2\log_2{suma_i})\)(第五个sub)

#include<bits/stdc++.h>
using namespace std;
#define N 110
int n,a,b,x[2010],mx;
long long ans=1e18,s[2010],h[2010];
bool f[N][N][2050],g[N][N];
int pd(long long x){
	memset(g,0,sizeof(g));
	g[0][0]=1;
	for(int i=0;i<n;i++)
		for(int j=0;j<b;j++)
			for(int k=i+1;k<=n;k++){
				long long v=s[k]-s[i];
				if((v&x)==v)
					g[k][j+1]|=g[i][j];
			}
	for(int i=a;i<=b;i++)
		if(g[n][i])
			return 1;
	return 0;
}
int p1(long long x){
	memset(h,127,sizeof(h));
	h[0]=0;
	for(int i=0;i<n;i++)
		for(int k=i+1;k<=n;k++){
			long long v=s[k]-s[i];
			if((v&x)==v)
				h[k]=min(h[k],h[i]+1);
		}
	return h[n];
}
signed main(){
	scanf("%d%d%d",&n,&a,&b);
	for(int i=1;i<=n;i++){
		scanf("%d",&x[i]);
		s[i]=s[i-1]+x[i];
		mx=max(mx,x[i]);
	}
	if(n<=20){
		for(int i=0;i<(1<<(n-1));i++){
			long long s=x[1],ct=0,sv=0;
			for(int j=0;j<n-1;j++){
				if(i&(1<<j))
					s+=x[j+2];
				else{
					ct++;
					sv|=s;
					s=x[j+2];
				}
			}
			sv|=s;
			ct++;
			if(a<=ct&&ct<=b)
				ans=min(ans,sv);
		}
		printf("%lld\n",ans);
		return 0;
	}
	if(mx<=20){
		f[0][0][0]=1;
		for(int i=0;i<n;i++)
			for(int j=0;j<=b;j++)
				for(int k=0;k<1024;k++)
					for(int l=i+1;l<=n;l++)
						if(f[i][j][k]){
							int v=s[l]-s[i];
							f[l][j+1][k|v]|=f[i][j][k];
						}
		int ans=1e9;
		for(int i=a;i<=b;i++)
			for(int j=0;j<2048;j++)
				if(f[n][i][j])
					ans=min(ans,j);
		printf("%d",ans);
		return 0;
	}
	if(n<=100){
		long long p=1,c=0,sv=0;
		while(p<=s[n]){
			p*=2;
			c++;
		}
		g[0][0]=1;
		for(int i=c-1;~i;i--){
			long long t=(1ll<<i)-1;
			if(!pd(sv+t))
				sv+=(1ll<<i);
		}
		printf("%lld\n",sv);
		return 0;
	}
	if(n>100){
		long long p=1,c=0,sv=0;
		while(p<=s[n]){
			p*=2;
			c++;
		}
		g[0][0]=1;
		for(int i=c-1;~i;i--){
			long long t=(1ll<<i)-1;
			if(p1(sv+t)>b)
				sv+=(1ll<<i);
		}
		printf("%lld\n",sv);
		return 0;
	}
}
posted @ 2020-12-01 10:01  celerity1  阅读(118)  评论(0编辑  收藏  举报