BZOJ2734 HNOI2012集合选数(状压dp)
完全想不到的第一步是构造一个矩阵,使得每行构成公比为3的等比数列,每列构成公比为2的等比数列。显然矩阵左上角的数决定了这个矩阵,只要其取遍所有既不被2也不被3整除的数那么所得矩阵的并就是所有的数了,并且显然不会有重复。
现在要满足题目要求只需要使在矩阵中选取的数不相邻。显然这可以用状压dp以4^n*m的复杂度搞出来。对于每一个矩阵都这样做一遍再乘起来就可以了。
看起来复杂度非常爆炸。不过冷静分析一下,这样做的复杂度往大了算是Σ4log3(n/i)*log2n,即Σ(n/i)*log34*log2n,也即复杂度不会超过O(nlog2n)。当然远远跑不满。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define P 1000000001 #define N 100010 int n,len[20],p[20],lg3[N],f[20][1<<17],q[1<<17],cnt=0,ans=1; int main() { #ifndef ONLINE_JUDGE freopen("bzoj2734.in","r",stdin); freopen("bzoj2734.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(); p[1]=1;for (int i=2;i<=15;i++) p[i]=p[i-1]*3; lg3[1]=1; for (int i=2;i<=n;i++) { lg3[i]=lg3[i-1]; if (p[lg3[i]+1]<=i) lg3[i]++; } for (int i=0;i<(1<<17);i++) { int x=i,last=0;q[++cnt]=i; while (x) { if ((x&1)&&last) {cnt--;break;} last=x&1;x>>=1; } } q[cnt+1]=1<<17; for (int i=1;i<=n;i++) if (i%2&&i%3) { int m; for (m=1;(i<<m-1)<=n;m++) len[m]=lg3[n/(i<<m-1)]; m--; f[0][1]=1; for (int k=1;k<=m;k++) for (int j=1;q[j]<(1<<len[k]);j++) { f[k][j]=0; for (int x=1;q[x]<(1<<len[k-1]);x++) if (!(q[j]&q[x])) f[k][j]=(f[k][j]+f[k-1][x])%P; } int tot=0; for (int j=1;q[j]<(1<<len[m]);j++) tot=(tot+f[m][j])%P; ans=1ll*ans*tot%P; } cout<<ans; return 0; }