[APIO2015]巴厘岛的雕塑[按位贪心+dp]

题意

给你长度为 \(n\) 的序列,要求分成 \(k\) 段连续非空的区间,求所有区间和的 \(or\) 最小值。

分析

  • 定义 \(f_{i,j}\) 表示前 \(i\) 个点分成 \(j\) 段的最小 \(or\) 是有问题的,因为可能有一位一定在后面出现而某一位没有必要在后面出现,这时贪心就出现了问题。

  • 考虑按位贪心,假设考虑到了第 \(b\) 位,定义 \(f_{i,j}\) 表示前 \(i\) 个点分成 \(j\) 段且满足 \(b\) 以上位的限制,第 \(b\) 位为 \(0\) 是否存在方案。如果这一位没有方法满足为 \(0\) 就设置成 \(1\).

  • 复杂度 \(O(60*n^3)\)

  • 对于最后一个 \(subtask\) 稍微改一下定义即可,复杂度 \(O(60*n^2)\)

代码

#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))	{if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
	return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=2004,inf=0x3f3f3f3f;
LL s[N];
int A,B,n;
namespace task1{
	int f[N];LL res=0;
	void solve(){
		for(int b=60;~b;--b){
			fill(f+1,f+1+n,inf);f[0]=0;
			rep(i,1,n)
			rep(k,0,i-1)
			if(f[k]!=inf&&( res>>b+1 | (s[i]-s[k]>>b+1)) ==res>>b+1  && !((s[i]-s[k]>>b)&1))
			Min(f[i],f[k]+1);
			if(f[n]!=inf&&f[n]<=B) continue;
			else res|=1ll<<b;
		}
		printf("%lld\n",res);
	}
}
namespace task2{
	bool f[N][N];LL res=0;
	void solve(){
		for(int b=60;~b;--b){
			memset(f,0,sizeof f); f[0][0]=1;
			rep(i,1,n)
			rep(j,1,i)
			rep(k,0,i-1){
				f[i][j]|=(f[k][j-1]&& ( res>>b+1 | (s[i]-s[k]>>b+1)) ==res>>b+1  && !((s[i]-s[k]>>b)&1));
			}
			bool fg=0;
			for(int i=A;i<=B;++i) fg|=f[n][i];
			if(!fg) res|=1ll<<b;
		}
		printf("%lld\n",res);
	}
}
int main(){
	n=gi(),A=gi(),B=gi();
	rep(i,1,n) s[i]=s[i-1]+gi();
	if(A==1) task1::solve();
	else task2::solve();
	return 0;
}
posted @ 2018-11-03 11:30  fwat  阅读(156)  评论(0编辑  收藏  举报