CF327E Axis Walking

题目传送门

一、题目大意

给你一个长度为n(1<=n<=24)的正整数序列S,再有k(0<=k<=2)个正整数。

求有多少种S的排列方式使得其任意前缀和不会成为那k个数里的任意一个。 答案对1e9+7取模。

二、题目解析

这题很明了,一看就是状压dp

对于那些前缀和我们可以累加取得

不妨设sum[i]表示当状态为i时的前缀和

那么显然有sum[i]=i中为1的位置的sum

对于那些sum[i]给定的数的状态显然要舍去

那么不妨再设f[i]为状态为i时的方案数

类似于sum的转移,显然有f[i]=i中为1的位置的f
发现这两者都需要访问那些状态i中的1的位置

那么对于这些状态i中那些为1的位置如何访问呢?

我们当然不能每次都让i>>1然后再判断这一位是否为1,因为这样太慢了

所以我们需要快速地访问那些1的位置

这时需要用到神奇(毒瘤)的位运算了

我们发现当i&lowbit(i)时恰好能达到这一点

它相当于把i最低位1以后的部分全部消去了,相当于加速了我们的查找过程

于是乎,这题就与普通的状压dp无异了

三、完整代码

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7;        //取模
const int N = 1 << 24;          //最多24个数字
int n;                          //实际n个数字
int k;                          //k个不允许出现的数字
int full;                       //将所有位置全部为1时的拉满状态,对应的十进制数
unordered_set<int> no_permit;   //有哪些数字是不允许出现在前缀和中的
int f[N];                       //f[i]表示状态为i时的方案数
LL sum[N];                      //sum[i]表示当状态为i时的前缀和

//一般状态压缩DP,n<=12,这里的n<=24,看来暴力的状态压缩要挂~
int lowbit(int x) {
    return x & (-x);
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> sum[1 << i]; //按二进制位置映射的十进制数来存储(状态压缩式的存储方法)
    //前缀和不允许出现的数字
    cin >> k;
    int x;
    for (int i = 0; i < k; i++)cin >> x, no_permit.insert(x);

    //状态为0时,表示一个全部都不选择,那么前缀和是0,肯定不在给定的k个正整数之中。因为正整数都大于0,这是一种唯一方案。
    f[0] = 1, full = (1 << n) - 1;//满状态
    //枚举现在的状态
    for (int i = 1; i <= full; i++) {
        //每次新的状态(sum[i]) = 仅有末尾1的状态 + 去掉末尾1的状态,而二者都在先前求过了,可以O(1)的求出sum[i] 的值
        sum[i] = sum[i & ~lowbit(i)] + sum[lowbit(i)];
        //sum[i] += sum[i - 1]; 这种传统方法是不可以的,因为sum数组并不是按一个个数字按次序保存的,而是按二进制数位方式保存的
        //这所以这样保存,是因为状态压缩DP,是需要枚举每一个数位,每个数位与传统方式的前缀和不符,需要特殊记忆一下这个方法
        //如果前缀和等于限定的数,那么是不合法的
        if (no_permit.count(sum[i]))continue;

        //快速找到所有位置为1的
        for (int j = i; j; j -= lowbit(j))//枚举可能的上一次状态
            f[i] = (f[i] + f[i & ~lowbit(j)]) % MOD; //去掉j位为1的 i & ~lowbit(j)
    }
    printf("%d\n", f[full]);
    return 0;
}
posted @   糖豆爸爸  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2018-02-10 大数据统计分析平台之三、Kibana安装和使用
2018-02-10 大数据统计分析平台之一、Kafka单机搭建
2012-02-10 在Linux下配置squid[转载,待测试]
2012-02-10 内网用ssh连接linux很慢
2012-02-10 centos5.5 64位提升sata硬盘速度
2012-02-10 CentOS 64bit密码正确却无法登录系统
2012-02-10 Linux的内存释放脚本
Live2D
点击右上角即可分享
微信分享提示