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;
}

  

 

posted @ 2017-03-02 09:48  ljh_2000  阅读(480)  评论(0编辑  收藏  举报