博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

hihoCoder挑战赛19 A.Rikka with Sequence(状压DP)

题目链接
比赛链接

\(Description\)

给定\(n\)个数\(a_i\),求有多少个子集满足:其异或和等于其所有数的AND。
\(n\leq 50,\ a_i\lt 2^{13}\)

\(Solution\)
参考:https://www.cnblogs.com/SovietPower/p/9781573.html

暴力:\(f[i][j][k]\)表示前\(i\)个数,与起来为\(j\),异或和为\(k\)的方案数。复杂度\(O(n*4^{13})\)

考虑位运算的性质,最后怎么得到某一位的1:&要求所有数这一位为1,^只需判这一位为1的数的奇偶性。
所以我们用13位三进制s表示13位01的状态(2表示全1,0/1表示奇偶性),再存一下选的数的个数。
这样DP就是\(O(n*3^{13})\)了。

但是直接\(f[i][s][0/1]\)不会写啊,求路过dalao教。。(拆状态好像也挺麻烦)

记异或和为\(x\),位与和为\(y\),因为是与,所以\(x\)再与\(y\)\(y\)是有关系的,也就是当选了奇数个数时,\(x\&y=y\);否则\(x\&y=0\)
那么暴力中的合法的\(j,k\)实际没有\(2^{13}*2^{13}\)那么多。
所有合法状态满足\(x\&y=y\)或是\(x\&y=0\),也就是\(y\)要么是\(x\)的子集,要么与\(x\)没有交集(别忘这种情况啊)。
因为有第二种情况所以只求异或和的所有子集不行。但再求一遍补集存状态也不对(不知道为什么)。

\(xx=x\&(\sim y)\),我们发现\(xx\)还是确定的?而且因为\(x,y\)的关系,选奇数个时\(x\)就是\(xx|y\),否则\(x=xx\)
我们枚举\(y\),再枚举\(\sim y\)的子集(要\(\&8191\))得到\(xx\)。(我也不知道怎么会想到用\(xx\)。。好神啊)
在DP的时候根据奇偶性把\(x\)转化出来就行了(得状态再\(\&(\sim y)\))。然后就可以同暴力直接转移。
状态数为\(O(3^{13})\)

答案是\(f[n][status(0,0)][0]+\sum_s f[n][status(s,s)][1]\)
复杂度也是\(O(n*3^{13})\)

DP数组也要longlong(随机的话倒也爆不了int)。
id[][]按枚举顺序确定下标会快近一倍。

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define all 8191
#define cnt 1594323
typedef long long LL;
const int N=8192+3,M=1594323+3;

int And[M],XX[M],id[N][N];
LL F[M][2],G[M][2];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
int Init()
{
	int n=0;
	for(int y=0; y<=all; ++y)//y
	{
		int ss=(~y)&all;
		for(int x=ss; ; x=(x-1)&ss)
		{
			id[y][x]=++n;
			XX[n]=x, And[n]=y;
			if(!x) break;
		}
	}
	return n;
}

int main()
{
//	const int all=8191;
//	const int cnt=1594323;
	Init();
	int n=read(); LL (*f)[2]=F,(*g)[2]=G;
	f[id[all][0]][0]=1;
	for(int i=1,ai; i<=n; ++i)
	{
		ai=read(), std::swap(f,g);
		memcpy(f,g,sizeof F);//f[i][s]=f[i-1][s]
		for(int j=1; j<=cnt; ++j)
			for(int k=0; k<2; ++k)
			{
				if(!g[j][k]) continue;
				int x=XX[j],y=And[j];
				k && (x|=y);
				x^=ai, y&=ai;
				x&=(~y);
				f[id[y][x]][k^1]+=g[j][k];
			}
	}
	LL ans=f[id[0][0]][0];
	for(int i=1; i<=cnt; ++i) if(!XX[i]) ans+=f[i][1];//x==y xx=0
	printf("%lld\n",ans);

	return 0;
}
posted @ 2018-10-13 07:55  SovietPower  阅读(199)  评论(2编辑  收藏  举报