[ZROI159]取石子游戏 动态规划

[ZROI159]取石子游戏 动态规划_第1张图片

[ZROI159]取石子游戏 动态规划_第2张图片

[ZROI159]取石子游戏 动态规划_第3张图片

  • 70pts
  • 就是求在一段序列中取出若干个数(不能全取完且取出的个数必须是d的倍数),使剩下的xor和为0的方案数
  • f[i][k][w]表示现在到了第i个数,取出的数的个数%d==k,没取出来的数xor和为0时方案数
  • 答案就是f[n][0][0]-(n%d==0) (不能全取完)
  • 对于另外20%的数据,发现所有数的本质不同的xor和最多有2^5个,预处理出来本质不同的xor和,在第三维枚举转移
  • 注意要使用滚动数组,每次要循环清零
#include
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int mod=1e9+7;
int f[2][10][1<<20],a[2000000],b[10],c[1000],n,d,cnt=0,tot=0;
void upd(int &a,int b){a+=b; a-= a>=mod? mod:0;}
map v;
void subtask1(){
	f[0][0][0]=1;
	rep(i,1,n){
		rep(k,0,d-1){
			rep(w,0,(1<<14)-1){
				f[i%2][k][w]=(f[i%2][k][w]+f[(i-1)%2][(k-1+d)%d][w])%mod;
				f[i%2][k][w^a[i]]=(f[i%2][k][w^a[i]]+f[(i-1)%2][k][w])%mod;
			}
		}
		rep(k,0,d-1)rep(w,0,(1<<14)-1)f[(i-1)%2][k][w]=f[(i-1)%2][(k-1+d)%d][w]=0;
	}
	cout<>=1;
	}
	if(v[tot]==0) c[++cnt]=tot,v[tot]=1;
}
void subtask2(){
	rep(i,0,(1<5)continue;
		b[++tot]=a[i],v[a[i]]=1;
	}
	v.clear();
	if(tot>5)subtask1();
	else subtask2();
	return 0;
}
  • 100pts
  • 考虑到序列的顺序和最后答案无关,把a从小到大排序,每次找到第一个(2^x)>=a[i],枚举0~2^(x+1)
  • 因为\sum a[i]<=10^7所以复杂度是对的
#include
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int mod=1e9+7;
int f[2][10][1<<21],a[2000000],n,d;
void upd(int &a,int b){a+=b; a-= a>=mod? mod:0;}
int main()
{
	//freopen("a.in","r",stdin);
	scanf("%d%d",&n,&d);
	rep(i,1,n)scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	f[0][0][0]=1;
	rep(i,1,n){
		int now=1;
		while(now<=a[i])now<<=1;
		rep(k,0,d-1){
			rep(w,0,now){
				upd(f[i%2][k][w],f[(i-1)%2][(k-1+d)%d][w]);
				upd(f[i%2][k][w^a[i]],f[(i-1)%2][k][w]);
			}
		}
		rep(k,0,d-1)rep(w,0,now)f[(i-1)%2][k][w]=f[(i-1)%2][(k-1+d)%d][w]=0;
	}
	cout<

 

你可能感兴趣的:(动态规划)