本文版权归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[N][N],last[N],f[N][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; }