当前位置: 代码迷 >> 综合 >> HDU多校第七场 1008 Heart —— 子集dp + 卡常
  详细解决方案

HDU多校第七场 1008 Heart —— 子集dp + 卡常

热度:27   发布时间:2024-01-09 10:39:09.0

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n n 条弹幕,每条弹幕有两个值 p p p b b b
     p p p 代表攻击力, b b b 代表由其二进制构成这条弹幕
    一个必杀技 A A A n n n 条弹幕的子集,且满足 u , v ∈ A , u ≠ v , b u ∩ b v = ? u,v∈A,u≠v, b_u∩b_v=? u,vA,u??=v,bu?bv?=?
    定义 S ( A ) = ∪ u ∈ A b u S(A)=∪_{u∈A}b_u S(A)=uA?bu? A A A 的攻击力为其所有弹幕的攻击力的乘积
     m m m 次查询,每次查询 x x x,求所有 A A A 的攻击力和,且 S ( A ) = x S(A)=x S(A)=x

解题思路:

    因为 0 ≤ b i < 2 21 0≤b_i<2^{21} 0bi?<221,顺而想到子集 d p dp dp
    设 d p [ i ] dp[i] dp[i] S ( A ) = i S(A)=i S(A)=i 时所有 A A A 的攻击力和
     d p [ i ] + = d p [ j ] ? d p [ k ] , j ? k = i , j & k = 0 dp[i] += dp[j] * dp[k],j \bigoplus\ k = i,j \& k = 0 dp[i]+=dp[j]?dp[k]j? k=ij&k=0
    然而这样计算的时间复杂度是 3 21 3 ^ {21} 321,本地跑远超过 20 20 20
    而且这样计算会重复计算,设 f [ i ] f[i] f[i] 为只由一条弹幕组成 i i i 的攻击力
    那么 d p [ i ] + = f [ j ] ? d p [ k ] , j ? k = i , j & k = 0 dp[i] += f[j] * dp[k],j \bigoplus k = i,j \& k = 0 dp[i]+=f[j]?dp[k]j?k=ij&k=0
    这样直接枚举所有子集仍然会重复计算,那么考虑限制一下 j j j
    设 l o w b i t ( i ) = l b lowbit(i) = lb lowbit(i)=lb,我们确保枚举的 j j j 满足 j & l b = l b j \& lb = lb j&lb=lb
    也就是将 i i i 的最低位给取出来, j j j 的这一位始终为 1 1 1,其他位继续枚举

    这样就完美的解决了重复计算的问题,而且因为枚举的子集状态位少了一位
    时间复杂度大概为 3 20 3 ^ {20} 320,当时测试了一下大概要 60 60 60 秒左右,但是要优化到 20 20 20 秒内
    简单的输入输出挂,变量名放到外面什么相信大家都会,然而这优化不了几秒,其他卡常方法如下:

    采用快速取模
    考虑将 d p dp dp 数组设为 l o n g long long l o n g long long,作乘的时候必须取模,但作加的时候就可以直接加,枚举完所有子集后再取模
    因为 j j j 始终包含了 i i i 的最低位,那么 d p [ k ] dp[k] dp[k] 就始终缺少这一位
    那么也就意味着,我们不需要处理所有的 d p dp dp 值,因为有的 d p dp dp 值不可能用到
    这些不可能用到的 d p dp dp 值就是 i i i 为奇数的情况,因为 d p [ k ] dp[k] dp[k] k k k 始终为偶数
    但是如果查询的 x x x 为奇数,我们就必须处理 d p [ x ] dp[x] dp[x]
    因此考虑将查询离线,将出现过的 x x x 记录为 v i s [ x ] = 1 vis[x] = 1 vis[x]=1
    若 i i i 为奇数且 v i s [ x ] = 0 vis[x] = 0 vis[x]=0,代表这个 d p [ i ] dp[i] dp[i] 可以直接跳过
    我们假设出题人的数据是随机造的,时间复杂度就会降到 3 20 × 3 4 3^{20} \times \frac{3}{4} 320×43?
    全部优化后本题只需要 18 18 18 秒内就能搞定!!!
    就不用学子集卷积了

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 1 << 23;
const int mod = 998244353;
int n, m, ans[maxn];
ll dp[maxn], f[maxn];const int MAXSIZE=100000;
char buf[MAXSIZE],*p1=buf,*p2=buf;
#define nc p1==p2&&(p2=(p1=buf)+fread\
(buf,1,MAXSIZE,stdin),p1==p2)?EOF:*p1++
inline void read(int &x) {bool f=0; char ch=nc; x=0;while(!isdigit(ch)) {if(ch=='-')f=1;ch=nc;}while(isdigit(ch)) x=x*10+ch-'0',ch=nc;if(f) x=-x;
}typedef unsigned long long ull;
typedef __uint128_t L;
struct FastMod {ull b, m;FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}inline ull reduce(const ull a) { return a - (ull)((L(m) * a) >> 64) * b;}
} F(mod);int f0 = 1, tmp, i, j, p, b, qry[maxn], vis[maxn];
int lb, now, mx = 1 << 21, x, jlb;
signed main() {read(n);for( i=1; i<=n; ++i) {read(p), read(b);if(b == 0) f0 = F.reduce(1ll * f0 * (1 + p));else {tmp = f[b] + p;if(tmp >= mod) dp[b] = f[b] = tmp - mod;else dp[b] = f[b] = tmp;}}dp[0] = 1; // 2097152read(m);for( i=1; i<=m; ++i) read(qry[i]), vis[qry[i]] = 1;for( i=1; i<mx; ++i) {if((i&1) && !vis[i]) continue;lb = i & (-i), now = i ^ lb;for( j=(now-1)&now; j; j=(j-1)&now) {jlb = j + lb;dp[i] += F.reduce(f[jlb] * dp[i - jlb]);}if (lb != i) dp[i] += F.reduce(f[lb] * dp[i - lb]);dp[i] = F.reduce(dp[i]);}for( i=1; i<=m; ++i) {x = qry[i];if(x == 0) ans[i] = F.reduce(f0 - 1 + mod);else ans[i] = F.reduce(dp[x] * f0);printf("%d\n", ans[i]);}
}
  相关解决方案