题目链接:点我啊╭(╯^╰)╮
题目大意:
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,v∈A,u=v,bu∩bv=∅
定义
S
(
A
)
=
∪
u
∈
A
b
u
S(A)=∪_{u∈A}b_u
S(A)=∪u∈Abu,
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}
0≤bi<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=i,j&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=i,j&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; // 2097152
read(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]);
}
}