P3226 [HNOI2012]集合选数 状压DP

题意:

题面

分析:

我们发现每个数 \(n\) 是否被选,只与 \(\frac{n}{3},\frac{n}{2},2n,3n\) 有关,那么我们考虑建一张表,表上每一行按照 \(\times 3\) 的方式递增,每一列按照 \(\times 2\) 的方式递增,那么对于同一张表,任意上下左右相邻的数都是不能选的,那么这样的表一共有 \(n-\frac{n}{2}-\frac{n}{3}+\frac{n}{2\times 3}\) 种,每一张表的转移是 \(\log_2 n\times 2^{2\times \log_3 n}\) 虽然理论复杂度达到了 \(5e10\) 但是实际跑下来十分优秀,可能因为后来的表变的极小,转移的复杂度大幅降低

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	int read()
	{
		int x=0,f=1;
		char ch=getchar();
		while(!isdigit(ch))
		{
			if(ch=='-') f=-1;
			ch=getchar();
		}
		while(isdigit(ch))
		{
			x=(x<<1)+(x<<3)+ch-48;
			ch=getchar();
		}
		return x*f;
	}
	
	const int maxn = 1e5+5;
	const int maxm = 5005;
	const int mod = 1e9+1;
	int n,r,sta[maxm],num[20],f[20][maxm];
	long long ans=1,sum;
	bool vis[maxn];
	
	void update(int x)
	{
		r=1;sum=0;
		memset(num,0,sizeof(num));
		memset(f,0,sizeof(f));
		for(int i=x;i<=n;i<<=1,r++) for(int j=i;j<=n;j*=3) num[r]++,vis[j]=true;
		for(int i=0;i<(1<<num[1]);i++) f[1][i]=sta[i];
		for(int i=2;i<r;i++)
		{
			for(int j=0;j<(1<<num[i-1]);j++)
			{
				for(int k=0;k<(1<<num[i]);k++)
				{
					if(sta[j]&&sta[k]&&!(j&k)) f[i][k]=(f[i][k]+f[i-1][j])%mod;
				}
			}
		}
		for(int i=0;i<(1<<num[r-1]);i++) sum=(sum+f[r-1][i])%mod;
		ans=ans*sum%mod;
	}
	
	void work()
	{
		n=read();
		for(int i=0;i<=(2<<11);i++) sta[i]=((i<<1)&i)?0:1;
		for(int i=1;i<=n;i++) if(!vis[i]) update(i);
		printf("%lld\n",ans);
	}

}

int main()
{
	zzc::work();
	return 0;
}

上一篇:「HNOI2012」三角形覆盖问题


下一篇:luogu_P3224 [HNOI2012]永无乡