bzoj 2734 集合悬殊 (状压dp)

大意: 给定n, 求集合{1,2,...n}的子集数, 满足若x在子集内, 则2x,3x不在子集内.

 

 

f(x)x除去所有因子2,3后的数, 那么对于所有f值相同的数可以划分为一个等价类, 对2的倍数和3的倍数建一个二维的表, 在表上做状压dp即可. 最后答案就为每个等价类方案的乘积.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+1;
 
const int N = 1e5+10;
int n, vis[N];
int a[30], s[1<<11];
int dp[2][1<<11];
 
int calc(int x) {
    memset(dp,0,sizeof dp);
    memset(a,0,sizeof a);
    int dx = 0, dy = 0;
    for (int i=1,t=x; t<=n; t*=2,++i) {
        for (int j=1,tt=t; tt<=n; tt*=3,++j) {
            (a[i]<<=1)|=1, vis[tt] = 1;
            dx = max(dx, i);
            dy = max(dy, j);
        }
    }
    int mx = (1<<dy)-1, cnt = 0;
    REP(i,0,mx) if (!(i&i<<1)) s[++cnt] = i;
    int cur = 0;
    dp[cur][1] = 1;
    REP(i,1,dx) {
        cur ^= 1;
        memset(dp[cur],0,sizeof dp[cur]);
        REP(j,1,cnt) if (dp[!cur][j]) {
            REP(k,1,cnt) if (!(s[j]&s[k])&&(s[k]|a[i])==a[i]) {
                dp[cur][k]=(dp[cur][k]+dp[!cur][j])%P;
            }
        }
    }
    int ans = 0;
    REP(i,1,cnt) ans=(ans+dp[cur][i])%P;
    return ans;
}
 
int main() {
    scanf("%d", &n);
    int ans = 1;
    REP(i,1,n) if (!vis[i]) ans=(ll)ans*calc(i)%P;
    printf("%d\n", ans);
}

 

posted @   uid001  阅读(124)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
阅读排行:
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· 赶AI大潮:在VSCode中使用DeepSeek及近百种模型的极简方法
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
点击右上角即可分享
微信分享提示