Luogu P3226 [HNOI2012]集合选数

题目
这题的构造还是挺妙的。
我们构造一个数表,左上角为\(1\),每个元素是上面那个元素的\(2\)倍,左边那个元素的\(3\)倍。
那么题目的要求就转化成了在数表中选一堆元素,保证没有元素相互之间是四相邻(上下左右)的。因为一个数表最多是\(11*17\)的,所以我们可以状压dp。
显然一个数表无法包含\(1\sim n\)的所有元素,我们把最小的未被包含的拿出来再建一个数表,直到没有未被包含的元素为止。
由乘法原理可知,总的答案等于每个数表的答案的乘积。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int P=1000000001;
int inc(int a,int b){a+=b;return a>=P? a-P:a;}
int mul(int a,int b){return 1ll*a*b%P;}
int n,vis[100001],flg[2049],num[19],f[19][2049],ans=1;
void work(int x)
{
    int i,j,k,m=1,sum=0;memset(f,0,sizeof f),memset(num,0,sizeof num);
    for(i=x;i<=n;i<<=1,++m) for(j=i;j<=n;j*=3) ++num[m],vis[j]=1;
    for(i=0;i<1<<num[1];++i) f[1][i]=flg[i];
    for(i=2;i<m;++i) for(j=0;j<1<<num[i-1];++j) for(k=0;k<1<<num[i];++k) if(flg[j]&&flg[k]&&!(j&k)) f[i][k]=inc(f[i][k],f[i-1][j]);
    for(i=0;i<1<<num[m-1];++i) sum=inc(sum,f[m-1][i]);
    ans=mul(ans,sum);
}
int main()
{
    int i;cin>>n;
    for(i=0;i<=2048;++i) flg[i]=i<<1&i? 0:1;
    for(i=1;i<=n;++i) if(!vis[i]) work(i);
    cout<<ans;
}

猜你喜欢

转载自www.cnblogs.com/cjoierShiina-Mashiro/p/11735195.html