zoukankan      html  css  js  c++  java
  • BZOJ2734 [HNOI2012]集合选数

    本文版权归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;
    }
    

      

  • 相关阅读:
    7.2对象的生命周期
    7.2.2垃圾收集和对象的终结
    7.1.2验证
    发现电脑上装着liteide,就用golang做一个TCP通讯测试(支持先启动client端和断线重连)
    C++读写局域网共享
    C++编写 动态链接库dll 和 调用dll
    VBA果然很强大
    [Windows]查看运行进程的参数【wmic】
    自旋锁-SpinLock(.NET 4.0+)
    C#并行和多线程编程
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6489018.html
Copyright © 2011-2022 走看看