P3226 [HNOI2012]集合选数 状压DP

题意:

题面

分析:

我们发现每个数 n n n 是否被选,只与 n 3 , n 2 , 2 n , 3 n \frac{n}{3},\frac{n}{2},2n,3n 3n,2n,2n,3n 有关,那么我们考虑建一张表,表上每一行按照 × 3 \times 3 ×3 的方式递增,每一列按照 × 2 \times 2 ×2 的方式递增,那么对于同一张表,任意上下左右相邻的数都是不能选的,那么这样的表一共有 n − n 2 − n 3 + n 2 × 3 n-\frac{n}{2}-\frac{n}{3}+\frac{n}{2\times 3} n2n3n+2×3n 种,每一张表的转移是 log ⁡ 2 n × 2 2 × log ⁡ 3 n \log_2 n\times 2^{2\times \log_3 n} log2n×22×log3n 虽然理论复杂度达到了 5 e 10 5e10 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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值