#轮廓线dp,模型转换#洛谷 3226 [HNOI2012]集合选数

题目

问有多少个集合 \(S\) 是 \([1,n]\) 的子集,

并且 \(\forall a,b\in S,a|b\),满足 \(\frac{b}{a}\neq \{2,3\}\)


分析

可以发现这样所谓的独立集,不满足的关系近似于 若干个网格图 ,即

1 3 9 ...
2 6 18 ...
4 12 36 ...
... ... ... ...

也就是相邻的不能选,可以发现直接状压dp就可以了,

然后表格左上角以 \(6i+1\) 和 \(6i+5\) 数开始即可


代码

#include <cstdio>
#include <cstring>
using namespace std;
const int N=2101,mod=1000000001;
int a[18][11],l[18],f[N],dp[N],n,al,two[18],tot[18],TOT,rk[N],b[N],ans=1;
int mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int answ(int m){
	if (n/m<3) return n/m+1;
	int k=1,ans=0; a[1][l[k]=1]=m;
	while (1){
	    while (a[k][l[k]]*3<=n) ++l[k],a[k][l[k]]=a[k][l[k]-1]*3;
	    if (a[k][1]*2<=n) l[++k]=1,a[k][1]=a[k-1][1]*2;
	        else break;
	}
	memset(dp,0,sizeof(dp)),
	memset(f,0,sizeof(f)),
	l[0]=l[1],dp[1]=1;
	for (int i=1;i<=k;++i){
		int now=0;
		for (int j=l[i];j<l[i-1];++j) now|=two[j];
		for (int j=1;j<=tot[l[i-1]];++j){
			int t0=rk[b[j]&(al^1)],t1=rk[b[j]|1];
			if (!(b[j]&1)) f[j]=mo(dp[t0],dp[t1]);
			    else if (!(now&1)) f[j]=dp[t0];
			        else f[j]=0;
		}
		memcpy(dp,f,(tot[l[i-1]]+1)<<2);
		for (int o=1;o<l[i-1];++o){
			for (int j=1;j<=tot[l[i-1]];++j){
				int t0=rk[b[j]&(al^two[o])],t1=rk[b[j]|two[o]];
				if (!((b[j]>>o)&1)) f[j]=mo(dp[t0],dp[t1]);
				    else if (!((b[j]>>(o-1))&1)&&!(now&1)) f[j]=dp[t0];
				        else f[j]=0;
			}
			memcpy(dp,f,(tot[l[i-1]]+1)<<2);
		}
	}
	for (int i=1;i<=tot[l[k]];++i) ans=mo(ans,dp[i]);
	return ans;
}
int main(){
	scanf("%d",&n),two[0]=rk[0]=TOT=1,al=2047;
	for (int i=1;i<12;++i) two[i]=two[i-1]<<1;
	for (int j=1;j<12;++j){
		for (int i=two[j-1];i<two[j];++i){
		    int now=i&(i<<1);
		    if (!(now&(now-1))) b[++TOT]=i,rk[i]=TOT;
	    }
	    tot[j]=TOT;
	}
	for (int i=0;i<=n;++i){
		if (6*i+1<=n) ans=1ll*ans*answ(6*i+1)%mod;
		    else break;
		if (6*i+5<=n) ans=1ll*ans*answ(6*i+5)%mod;
		    else break;
	}
	return !printf("%d",ans);
} 
上一篇:P1975 [国家集训队]排队 题解


下一篇:ARC111