【bzoj2734】【HNOI2012】【集合选数】【状压dp】

探讨了一道算法题,要求计算满足特定条件的子集数量。通过将问题转化为矩阵形式,并采用状态压缩动态规划的方法解决,最终给出了C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。 
 

Input

 只有一行,其中有一个正整数 n,30%的数据满足 n≤20。 
 

Output


 仅包含一个正整数,表示{1, 2,..., n}有多少个满足上述约束条件 的子集。 
 

Sample Input


4

Sample Output

8

【样例解释】

有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。
题解:我们把x之间的关系写成一些矩阵。
x 3x 9x ...
2x 6x 18x ...
4x 12x 24x ...
...
现在问题变成了给你一些矩阵,要求不能选相邻的数,求方案数。
显然这个矩阵的长宽是log级的。
所以对于每个矩阵直接状压dp
最后把所有矩阵的答案乘起来即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define P 1000000001
using namespace std;
int f[2][1<<12],n,vis[1<<12];
long long ans(1);
int cal(int x){
  int i,j,t,a,b,pre(0),ans(0);
  memset(f,0,sizeof(f));
  f[1][0]=1;
  for (i=0;x*(1<<i)<=n;i++){
    memset(f+(i&1),0,sizeof(f)>>1);
    for (j=0,t=1;x*t*(1<<i)<=n;j++,t*=3);
    for (a=0;a<(1<<pre);a++)
     if (vis[a])
      for (b=0;b<(1<<j);b++)
        if (vis[b]&&((a&b)==0))
          f[i&1][b]+=f[~i&1][a],f[i&1][b]%=P;
   pre=j;
  }
 //cout<<f[0][0]<<' '<<f[0][1]<<endl;
 for (j=0;j<(1<<pre);j++)
  ans+=f[~i&1][j],ans%=P;
 return ans;
}
int main(){
 scanf("%d",&n);
 for (int i=0;i<(1<<12);i++)
   if (!(i<<1&i)&&!(i>>1&i)) 
     vis[i]=1;
 for (int i=1;i<=n;i++)
  if (i%2&&i%3)
    ans*=cal(i),ans%=P; 
 cout<<ans<<endl;
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值