您的位置:首页 > 其它

BZOJ2734 [HNOI2012]集合选数

2017-03-02 09:48 281 查看
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

题目链接:BZOJ2734

正解:状压DP

解题报告:

  这道题思路好神啊…

  考虑我选择了$x$就不能选择$2*x$和$3*x$,所以我需要想办法把这个“不能选择”的关系,想办法变成我们可以做的模型。

  我们构造一个矩阵,$a[1][1]=1$,矩阵中满足$a[i][j]=a[i-1][j]*2$ && $a[i][j]=a[i][j-1]*3$,我们发现题目中的约束条件,被我们转换成了不能选择矩阵中相邻的数的选数的方案数。

  $2^{17}$$>$$100000$,所以矩阵行列最大也才$17$,我们考虑状压$DP$。

  预处理出每行的合法状态,相邻两行的合法转移,就可以直接做了。

  但是可能有一些数没有被我们覆盖,那么每次找到最小的我还没做过的数来做,想一想会发现肯定不会重复,而且每个数只会被提出来一次。

  每次用乘法原理把不同矩阵的方案乘起来就可以了,容易发现下一个矩阵的$0$状态就相当于是全局中没选它,所以正确性显然。

  

  复杂度的话,我写的时候觉得很不靠谱...

  但是上界远远不到,而且行列长度减小的很快,合法状态又相当少,“不到上界大法”吼啊!(雾

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
typedef long double LB;
typedef complex<double> C;
const double pi = acos(-1);
const int mod = 1000000001;
const int MAXN = 100011;
const int N = 18;
const int S = 132011;
int n,hang,lie,a

,last
,f
[S];
LL ans,tot;
bool vis[MAXN],ok[S];

inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline bool check(int x){
int lst=0,nn;
while(x>0) {
nn=x&1; if(nn==1 && lst==1) return false;
lst=nn;
x>>=1;
}
return true;
}

inline void work(){
n=getint(); ans=1; int pos=1,end;
for(int i=(1<<17)-1;i>=0;i--) ok[i]=check(i);
while(pos<=n) {
a[1][1]=pos; hang=lie=1; vis[pos]=1;
while(a[1][lie]*3<=n) lie++,a[1][lie]=a[1][lie-1]*3,vis[a[1][lie]]=1;
while(a[hang][1]*2<=n) hang++,last[hang]=1,a[hang][1]=a[hang-1][1]*2,vis[a[hang][1]]=1;
last[1]=lie;
for(int i=2;i<=hang;i++)
for(int j=2;j<=lie;j++) {
a[i][j]=a[i][j-1]*3;
if(a[i][j]>n) { a[i][j]=0; break; }
last[i]=j; vis[a[i][j]]=1;
}
end=(1<<lie)-1;
for(int i=end;i>=0;i--) if(ok[i]) f[1][i]=1; else f[1][i]=0;

for(int i=2;i<=hang;i++) {
end=(1<<last[i])-1;	for(int j=end;j>=0;j--) f[i][j]=0;
for(int j=(1<<last[i-1])-1;j>=0;j--) {
if(f[i-1][j]==0) continue;
for(int to=0;to<=end;to++) {
if(!ok[to]) continue;
if((j&to)!=0) continue;
f[i][to]+=f[i-1][j];
f[i][to]%=mod;
}
}
}
tot=0; for(int i=(1<<last[hang])-1;i>=0;i--) tot+=f[hang][i],tot%=mod;
ans*=tot;
ans%=mod;

while(vis[pos]) pos++;
}
printf("%lld",ans);
}

int main()
{
work();
return 0;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: