zoukankan      html  css  js  c++  java
  • 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^3log_2{suma_i}))(第四个sub)或者(O(n^2log_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
    ",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
    ",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
    ",sv);
    		return 0;
    	}
    }
    
  • 相关阅读:
    C++ 内存分配(new,operator new)详解
    单例
    实现sizeof
    muduo学习一:简介
    虚函数可以是内联函数吗?
    C++多态实现机制
    [转] Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用
    [Android]实现客户端之间的即时通信
    android获取手机通讯录
    java中forName()的作用
  • 原文地址:https://www.cnblogs.com/ctmlpfs/p/14066477.html
Copyright © 2011-2022 走看看