【状压DP】BZOJ2734 [HNOI2012]集合选数
题面在这里
一开始看毫无头绪,可以建立这样一个矩阵: \[
x\ \ 3x\ \ 9x \dots \\
2x\ \ 6x\ \ 18x \dots \\
4x\ \ 12x\ \ 36x \dots \\
\dots
\] 然后就是不能取相邻格子,因为\(logn\)比较小,直接状压就好了……
复杂度有点玄学啊……
卡了好久才过
示例程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include<cstdio> #include<cstring> #define cl(x,y) memset(x,y,sizeof(x)) typedef long long ll;
const int MOD=1e9+1,maxn=20,maxm=13,maxs=1<<13; int n,e,N,M; ll f[maxn][maxs],pw2[maxn],pw3[maxn],ans; void maker(){ N=M=1;pw2[0]=pw3[0]=1; for (int i=1;pw2[i-1]*2<=n;i++) pw2[i]=pw2[i-1]*2,N++; for (int i=1;pw3[i-1]*3<=n;i++) pw3[i]=pw3[i-1]*3,M++; } int highbit(int j) {for (int t=M;t;t--) if (j&(1<<t-1)) return t;return 0;} ll DP(int x){ N=M=1; for (int i=x;i*2<=n;i*=2) N++; for (int i=x;i*3<=n;i*=3) M++; for (int i=1;i<=N;i++) for (int j=0;j<(1<<M);j++) f[i][j]=0; f[0][0]=1; for (int i=1;i<=N;i++) for (int j=0;j<(1<<M);j++){ if (j&(j>>1)) continue;ll w=pw2[i-1]*pw3[highbit(j)-1]*(j!=0); if (w>n||x*w>n) break; for (int k=0;k<(1<<M);k++) if (!(k&j)) (f[i][j]+=f[i-1][k])%=MOD; } ll res=0; for (int j=0;j<(1<<M);j++) (res+=f[N][j])%=MOD; return res; } int main(){ scanf("%d",&n); maker(); ans=1; for (int i=1;i<=n;i++) if (i%2!=0&&i%3!=0) (ans*=DP(i))%=MOD; printf("%lld",ans); return 0; }
|