题目
思路
很明显我们有 2 c n t ? 2^{cnt_?} 2cnt? 的做法。
然后我们继续想办法推广,我们似乎能够很好的处理这个式子
A x ′ = ∑ s ⊆ x A s A'_x=\sum_{s\subseteq x}A_s Ax′=s⊆x∑As
然后我们要是把 ? ? ? 当成一,把这个式子拿去直接求不就行了?
譬如,我们有 ? 01 ?01 ?01 这个数,我们当成 101 101 101 求子集,我们就有这几个数
- 001 001 001、 101 101 101
- 000 000 000、 100 100 100
注意第二排: 1 1 1 被当成 0 0 0 了,蛮糟糕的。只好容斥,会是 2 c n t 1 2^{cnt_1} 2cnt1 的复杂度。
哦,类似的,我们也可以 2 c n t 0 2^{cnt_0} 2cnt0 作容斥!
于是我们用 O ( 2 min ( c n t ? , c n t 0 , c n t 1 ) ) \mathcal O(2^{\min(cnt_?,cnt_0,cnt_1)}) O(2min(cnt?,cnt0,cnt1)) 去做,不难发现 min ≤ 6 \min\le 6 min≤6 ,故可通过。
详见此博客,讲得更详细。
代码
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 20;
int a[1<<MaxN];
int f[1<<MaxN]; // 子集的和
int g[1<<MaxN]; // fwt_and 正变换
int bc[1<<MaxN]; // bitcount
void fwt(int n){
for(int i=0; i<n; ++i)
for(int j=0; j<(1<<n); ++j)
if(j&(1<<i)){
f[j] += f[j^(1<<i)];
g[j^(1<<i)] += g[j];
}
}
bool valid(char c){
if(c == '1') return true;
if(c == '0') return true;
return c == '?';
}
# define sgn(x) ((bc[x]&1)?-1:1)
char s[1<<MaxN|1];
int main(){
int n = readint(), q = readint();
for(int i=1; i<(1<<n); ++i)
bc[i] = bc[i-(i&-i)]+1;
scanf("%s",s);
for(int i=0; i<(1<<n); ++i)
a[i] = f[i] = g[i] = s[i]-'0';
for(fwt(n); q; --q){
int x = 0, y = 0, z = 0, ans;
char c = getchar();
while(not valid(c))
c = getchar();
for(; valid(c); c=getchar()){
x = (x<<1)|(c == '0');
y = (y<<1)|(c == '1');
z = (z<<1)|(c == '?');
}
if(bc[x] <= 6){
ans = g[y];
for(int s=x; s; s=(s-1)&x)
ans += sgn(s)*g[s^y];
}
else if(bc[y] <= 6){
int now = ((1<<n)-1)^x;
ans = f[now];
for(int s=y; s; s=(s-1)&y)
ans += sgn(s)*f[now^s];
}
else if(bc[z] <= 6){
ans = a[y];
for(int s=z; s; s=(s-1)&z)
ans += a[s|y];
}
printf("%d\n",ans);
}
return 0;
}
后记
感谢同一机房的大佬@Arextre的勘误,我要努力向您看齐!