【BZOJ】2734: [HNOI2012]集合选数

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2734


 

考虑$N=4$的情况:

\begin{bmatrix}
1&3 &X \\
2&X &X \\
4&X &X
\end{bmatrix}

其实就是吧最小值丢在了矩阵中${(0,0)}$的位置上,对于矩阵中的任意位置令${f[i][j]=f[i][j-1]*3}$,${f[i][j]=f[i-1][j]*2}$。

这样一来问题就转换为了:在一个矩阵中选取任意多的数字使得没有两个相邻的数字被同时选取的方案数。这个就是经典的轮廓线DP模型。

但是我们注意到并不是一个矩阵就覆盖了所有的数字,所以我们需要记录哪些数字已经在矩阵中出现过了,从小到大枚举数字,对于还没有出现的数字再把它丢到${(0,0)}$的位置在构造一个矩阵并进行DP。利用乘法原理计算贡献,易证一个数字有且仅有在一个矩阵中出现,所以这样就可以打成补充不漏的效果。


 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<vector>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<cstring>
 8 using namespace std;
 9 #define maxn 1001000
10 #define llg long long 
11 #define md 1000000001
12 #define yyj(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
13 llg n,m,N,ans,flag[maxn];
14 llg val[110][110],f[2][(1<<12)];
15 llg work(llg S)
16 {
17     memset(val,0,sizeof(val));
18     n=m=0;
19     flag[S]=1;
20     llg x=S;
21     val[0][0]=S;
22     while (x*2<=N) {n++; val[n][0]=val[n-1][0]*2; flag[val[n][0]]=1; x*=2; }
23     x=S;
24     while (x*3<=N) {m++; val[0][m]=val[0][m-1]*3; flag[val[0][m]]=1; x*=3;}
25     for (llg i=1;i<=n;i++)
26         for (llg j=1;j<=m;j++)
27         {
28             if (val[i][j-1]*3>N) break;
29             val[i][j]=val[i][j-1]*3;
30             flag[val[i][j]]=1;
31         }
32     llg la=0,now;
33     memset(f,0,sizeof(f));
34     f[la][0]=1;
35     for (llg i=0;i<=n;i++)
36         for (llg j=0;j<=m;j++)
37         {
38             now=la^1;
39             memset(f[now],0,sizeof(f[now]));
40             for (llg zt=0;zt<=(1<<(m+1));zt++)
41                 if (f[la][zt]!=0)
42                 {
43                     llg nzt=zt;
44                     f[la][zt]%=md;
45                     if (nzt&(1<<j)) nzt-=(1<<j);
46                     f[now][nzt]+=f[la][zt];
47                     if ((val[i][j]==0) || (zt&(1<<j))) continue;
48                     if (j!=0)
49                     {
50                         if (zt&(1<<(j-1))) continue;
51                     }
52                     nzt=zt;
53                     nzt|=(1<<j);
54                     f[now][nzt]+=f[la][zt];
55                 }
56             la=now;
57         }
58     llg tot=0;
59             for (llg i=0;i<=(1<<(m+1));i++) tot+=f[now][i];
60     tot%=md;
61     return tot;
62 }
63 
64 int main()
65 {
66     yyj("a");
67     cin>>N;
68     ans=1;
69     for (llg i=1;i<=N;i++)
70         if (!flag[i])
71         {
72             ans*=work(i);
73             ans%=md;
74         }
75     cout<<ans;
76     return 0;
77 }

 

posted @ 2017-02-13 20:53  №〓→龙光←  阅读(202)  评论(0编辑  收藏  举报