版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/82049630
题目大意:
给定一个集合,里面为1-n的数,现在要求从中选择一些数,选了x就不可以选2x和3x,求方案数。
思路:
考虑根据题目的限制关系构造一个矩阵。
每一行里面的关系是 ,每一列里面的关系是 ,不难发现这个矩阵里面相邻的数我们都不可以选择,所以可以用状压DP来解决。
如果我们把x设为1,那么肯定还有很多数没有包括在这个里面,其实没有包括在这个里面的数和这个矩阵没有任何约数的关系,它们互相不可到达,同时每一个数也只会在一个矩阵里面。所以我们可以对于没有在这里面的数再构造若干个矩阵,直至所有的数都计算完为止。
最后的答案为若干个矩阵的答案之积。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2734.in","r",stdin);
freopen("bzoj2734.out","w",stdout);
}
void out(int x,int len){
int qu[70]={0},q=0;
while(len--)qu[++q]=x%2,x/=2;
DREP(i,q,1)printf("%d ",qu[i]);
putchar('\n');
}
const int maxn=1e5+10;
const int maxb=20;
const int maxw=(1<<18)+10;
const ll mod=1e9+1;
int n,h,len[maxb],a[maxb][maxb];
int all[maxb],st[maxw],tot;
ll dp[maxb][maxw],ans=1;
bool usd[maxn];
void add(ll &_,ll __){_=_+__;if(_>mod)_-=mod;}
ll solve(int x){
h=1; a[1][1]=x;
while(a[h][1]*3<=n)a[h+1][1]=a[h][1]*3,++h;
REP(i,1,h){
len[i]=1;
while(a[i][len[i]]*2<=n)a[i][len[i]+1]=a[i][len[i]]*2,++len[i];
all[i]=(1<<len[i])-1;
}
REP(i,1,h)REP(j,1,len[i])usd[a[i][j]]=1;
dp[0][0]=1;
REP(i,1,h){
REP(k,1,tot){
if(st[k]>all[i])break;
dp[i][st[k]]=0;
REP(j,1,tot){
if(st[j]>all[i-1])break;
if(!(st[j]&st[k]))
add(dp[i][st[k]],dp[i-1][st[j]]);
}
}
}
ll ret=0;
REP(i,1,tot){
if(st[i]>all[h])break;
add(ret,dp[h][st[i]]);
}
return ret;
}
int main(){
File();
scanf("%d",&n);
REP(S,0,(1<<17)-1){
bool flag=true;
REP(i,2,17)if(((1<<(i-1))&S) && ((1<<(i-2))&S))
flag=false;
if(flag)st[++tot]=S;
}
REP(i,1,n)if(!usd[i]){
usd[i]=1;
ans=ans*solve(i)%mod;
}
printf("%lld\n",ans);
return 0;
}