您的位置:首页 > 其它

BZOJ 2734 HNOI2012 集合选数 状压DP

2014-10-15 20:19 211 查看
题目大意:给定n,求集合S={1,2,3,...,n}有多少子集满足对于任意集合中任意两个数x和y,x≠2y并且x≠3y

原题解见 http://www.cnblogs.com/Randolph87/p/3677786.html
对于n以内任意与6互质的整数x,我们列出一个矩阵

x 3x 9x 27x ...

2x 6x 18x 54x ...

4x 12x 36x 108x ...

...

向下是*2,向右是*3,这样这个矩阵的任意两个相邻的数都不能出现在同一集合中

方案数状压DP即可 最后把所有x的矩阵方案数用乘法原理乘在一起即可

很巧妙的一道题

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Mo 1000000001
using namespace std;
int n,f[2][1<<12];
bool usable[1<<12];
long long ans=1;
int State_Compression_DP(int now)
{
	int i,j,s1,s2,last=0,re=0;
	memset(f,0,sizeof f);
	f[1][0]=1;
	for(i=0;now*(1<<i)<=n;i++)
	{
		int temp;
		memset(f+(i&1),0,sizeof(f)>>1 );
		for(j=0,temp=1;now*(1<<i)*temp<=n;j++,temp*=3);
		for(s1=0;s1<1<<last;s1++)
			if(usable[s1])
				for(s2=0;s2<1<<j;s2++)
					if(usable[s2])
						if( (s1&s2)==0 )
							f[i&1][s2]+=f[~i&1][s1],f[i&1][s2]%=Mo;
		last=j;
	}
	for(j=0;j<1<<last;j++)
		re+=f[~i&1][j],re%=Mo;
	return re;
}
int main()
{
	int i;
	cin>>n;
	for(i=0;i<1<<12;i++)
		if( !(i>>1&i) && !(i<<1&i) )
			usable[i]=1;
	for(i=1;i<=n;i++)
		if(i%2&&i%3)
			ans*=State_Compression_DP(i),ans%=Mo;
	printf("%d\n",(int)ans);
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: