luogu P3226 [HNOI2012]集合选数

luogu

因为限制关系只和2和3有关,如果把数中2的因子和3的因子都除掉,那剩下的数不同的数是不会相互影响,所以每次考虑剩下的数一样的一类数,答案为每类数答案的乘积

如果选了一个数,那么2的因子多1的和3的因子多1的数都不能选.假设这个数为\(2^a3^bc\),那就把这个数放在\(i\)\(j\)列上,现在问题变成这一堆数有多少子集满足没有两个上下或左右相邻元素,那么状压一行的放数状态,一行一行扫过去dp即可

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=1e5+10,M=(1<<16)+10,mod=1e9+1;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
void ad(int &x,int y){x+=y,x-=x>=mod?mod:0;}
int n,f[2][M],nm[20],t,ans=1,s[M],ts;
vector<int> sq[N];

int main()
{
	n=rd();
	for(int i=1;i<=n;++i)
	{
		int x=i;
		while(x%2==0) x/=2;
		while(x%3==0) x/=3;
		sq[x].push_back(i/x);
	}
	for(int i=0;i<1<<16;++i)
	{
		bool ok=1;
		for(int j=0;ok&&j<15;++j) ok=!(i>>j&1)||!(i>>(j+1)&1);
		if(ok) s[++ts]=i;
	}
	for(int i=1;i<=n;++i)
	{
		if(sq[i].empty()) continue;
		vector<int>::iterator it;
		memset(nm,0,sizeof(nm));
		for(it=sq[i].begin();it!=sq[i].end();++it)
		{
			int x=*it,c2=1;
			while(x%2==0) ++c2,x/=2;
			++nm[c2];
		}
		t=0;
		while(nm[t+1]) ++t;
		int nw=1,la=0;
		f[la][0]=1;
		for(int j=1;j<=t;++j)
		{
			for(int k=1;s[k]<1<<nm[j-1];++k)
			{
				if(!f[la][s[k]]) continue;
				for(int l=1;s[l]<1<<nm[j];++l)
					if(!(s[k]&s[l])) ad(f[nw][s[l]],f[la][s[k]]);
				f[la][s[k]]=0;
			}
			nw^=1,la^=1;
		}
		int sm=0;
		for(int k=1;s[k]<1<<nm[t];++k)
			ad(sm,f[la][s[k]]),f[la][s[k]]=0;
		ans=1ll*ans*sm%mod;
	}
	printf("%d\n",ans);
    return 0;
}
posted @ 2019-09-25 21:27  ✡smy✡  阅读(98)  评论(0编辑  收藏  举报