【BZOJ4347】[POI2016]Nim z utrudnieniem DP

【BZOJ4347】[POI2016]Nim z utrudnieniem

Description

A和B两个人玩游戏,一共有m颗石子,A把它们分成了n堆,每堆石子数分别为a[1],a[2],...,a[n],每轮可以选择一堆石子,取掉任意颗石子,但不能不取。谁先不能操作,谁就输了。在游戏开始前,B可以扔掉若干堆石子,但是必须保证扔掉的堆数是d的倍数,且不能扔掉所有石子。A先手,请问B有多少种扔的方式,使得B能够获胜。

Input

第一行包含两个正整数n,d(1<=n<=500000,1<=d<=10)。
第二行包含n个正整数a[1],a[2],...,a[n](1<=a[i]<=1000000)。
本题中m不直接给出,但是保证m<=10000000。

Output

输出一行一个整数,即方案数对10^9+7取模的结果。

Sample Input

5 2
1 3 4 1 2

Sample Output

2

题解:一开始没看到m是所有石子个数的和,GG。。。

Nim游戏结论(说n遍了):所有堆的石子数异或和不为0则先手必胜,否则先手必败。

这个有什么用?我们将所有堆从小到大排序,对于一个数x,它和比它小的数中任意一些数的异或和不会超过2*x,所以直接DP,用f[i][j]表示当前异或和为i,选出石子的堆数%d=j的方案数,答案就是f[0][n%d](也可以是f[sum][0])

但是TLE啊,狂T不止啊,要了数据手动测了30个点都没问题啊,不就是std新开了一个数组temp,而我吧temp和f数组合到一起了吗~

还好有网上的黑科技,将f[i][j]换成f[j][i]就过了,从TLE变成14s。

顿时觉得二位数组的复杂度真的玄学,估计那天丧心病狂开它12个一维数组没准就rank1了?实在不行写循环展开?(orz wys!orz wys!orz wys! O(wys)!O(wys)!O(wys)!)

#include <cstdio>
#include <algorithm>
#define mod 1000000007
using namespace std;
int n,m,d,ans;
int v[500010],f[12][1<<20];
int rd()
{
	int ret=0;	char gc=getchar();
	while(gc<'0'||gc>'9')	gc=getchar();
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret;
}
int main()
{
	n=rd(),d=rd();
	int i,j,k,x;
	for(i=1;i<=n;i++)	v[i]=rd();
	sort(v+1,v+n+1),f[0][0]=1;
	for(i=m=1;i<=n;i++)
	{
		x=v[i];
		while(m<=x)	m<<=1;
		for(k=0;k<m;k++)	f[d][k]=f[d-1][k];
		for(j=d-1;j;j--)	for(k=0;k<m;k++)
		{
			f[j][k]+=f[j-1][k^x];
			if(f[j][k]>=mod)	f[j][k]-=mod;
		}
		for(k=0;k<m;k++)
		{
			f[0][k]+=f[d][k^x];
			if(f[0][k]>=mod)	f[0][k]-=mod;
		}
	}
	ans=f[n%d][0];
	if(n%d==0)	ans--;
	if(ans<0)	ans+=mod;
	printf("%d",ans);
	return 0;
}
posted @ 2017-06-20 15:56  CQzhangyu  阅读(249)  评论(0编辑  收藏  举报