您的位置:首页 > 其它

bzoj 2734 集合选数

2015-11-16 10:40 309 查看
构造矩阵1 3 9 27…2 6 18 54…4 12 36 108………每个数是上面的数乘2,左面的数乘3。这样进行状压dp就是相邻的格子不能选的方案数。如何判断一个二进制数没有两个连续的1? x&(x<<1)=0如果有的数没有出现过,就以它为左上角元素再构造一个矩阵。这是怎么想到的?!!
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>

#define ll long long
#define inf 1e9
#define eps 1e-10
#define md 1000000001
#define N 
using namespace std;
int a[20][15],b[20],mi[20];
ll f[20][5010];
bool vis[100010];
int n;
ll ans=1;
void add(ll &x,ll y)
{
	x=x+y; if (x>=md) x-=md;
}
void solve(int x)
{
	for (int i=1,now=x;i<=18;i++,now*=2)
	{
		if (now<=n)
		{
			a[i][1]=now;
			vis[now]=1;
			b[i]=mi[1];
		}
		else 
		{ 
			a[i][1]=n+1;
			b[i]=0;
			now=n+1;
		}
	}
	for (int i=1;i<=18;i++)
	  for (int j=2;j<=11;j++)
	  {
	  	int x=a[i][j-1]*3;
	  	if (x<=n)
	  	{
	  		a[i][j]=x;
	  		vis[x]=1;
	  		b[i]+=mi[j];
	  	}
	  	else
	  	{
	  		a[i][j]=n+1;
	  	}
	  }
	for (int i=1;i<=18;i++)
	  for (int j=0;j<=b[i];j++)
	    f[i][j]=0;
	b[0]=1; f[0][0]=1;
	for (int i=0;i<18;i++)
	{
		for (int j=0;j<=b[i];j++)
		  if (f[i][j])
		  {
		  	for (int k=0;k<=b[i+1];k++)
		  	  if ( (!(k&(k<<1))) && (!(j&k))) add(f[i+1][k],f[i][j]);
		  }
	}
	ans=ans*f[18][0]%md;
}

int main()
{
	mi[1]=1; for (int i=2;i<=18;i++) mi[i]=mi[i-1]<<1;
	ans=1;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	  if (!vis[i]) solve(i);
	printf("%lld\n",ans);
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: