【洛谷7670】[JOI2018] Snake Escaping(数据分治)
- 给定 \(a_{0\sim 2^n-1}\)。\(q\) 次询问,每次给出一个由
0
,1
,?
构成的长度为 \(n\) 的字符串,其中?
可以替换为0
或1
,求能得到的所有 \(n\) 位二进制数 \(x\) 对应的 \(a_x\) 之和。 - \(1\le n\le20\),\(1\le q\le10^6\)
针对三种字符的数据分治做法
假设 0
,1
,?
三种字符的个数分别为 \(c_0,c_1,c_2\),考虑三种不同的做法:
- 暴力枚举每个
?
填成0
还是1
,复杂度 \(O(2^{c_2})\)。 - 对
0
容斥,用这位为?
的答案减去这位为1
的答案(由于仅存在1
,?
两种字符,可以高维前缀和预处理),复杂度 \(O(2^{c_0})\)。 - 对
1
容斥,用这位为?
的答案减去这位为0
的答案(由于仅存在0
,?
两种字符,可以高维前缀和预处理),复杂度 \(O(2^{c_1})\)。
三者取最优的那个,复杂度 \(O(2^{\min\{c_0,c_1,c_2\}})\),最坏情况下是 \(O(2^{\frac n3})\)。
代码:\(O(n2^n+qn2^{\frac n3})\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 20
using namespace std;
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
I void readd(int& x) {W(isspace(oc=tc()));x=isdigit(oc)?oc&15:2;}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
int n,ans,a[1<<N],f0[1<<N],f1[1<<N],s[N+5],m,q[N+5];
I void dfs0(CI x,CI o,CI u=1) {if(x>m) return (void)(ans+=u*f0[o]);dfs0(x+1,o,u),dfs0(x+1,o|(1<<n-q[x]),-u);}//对0容斥
I void dfs1(CI x,CI o,CI u=1) {if(x>m) return (void)(ans+=u*f1[o]);dfs1(x+1,o,-u),dfs1(x+1,o|(1<<n-q[x]),u);}//对1容斥
I void dfs2(CI x,CI o) {if(x>m) return (void)(ans+=a[o]);dfs2(x+1,o),dfs2(x+1,o|(1<<n-q[x]));}//枚举?填法
I int Get(CI x) {RI i,t=0;for(m=0,i=1;i<=n;++i) s[i]^x?t|=(s[i]^2?s[i]:x)<<n-i:q[++m]=i;return t;}//拎出所有x,先状压好已知位
int main()
{
RI Qt,i,j,l;for(read(n,Qt),l=1<<n,i=0;i^l;++i) readd(a[i]),f0[i]=f1[i]=a[i];
for(j=0;j^n;++j) for(i=l-1;~i;--i) (i>>j&1)&&(f0[i^(1<<j)]+=f0[i]);//1,?时高维前缀和
for(j=0;j^n;++j) for(i=0;i^l;++i) !(i>>j&1)&&(f1[i^(1<<j)]+=f1[i]);//0,?时高维前缀和
RI c[3];W(Qt--)
{
for(c[0]=c[1]=c[2]=0,i=1;i<=n;++i) readd(s[i]),++c[s[i]];ans=0;
c[0]<=n/3?dfs0(1,Get(0)):(c[1]<=n/3?dfs1(1,Get(1)):dfs2(1,Get(2))),writeln(ans);//数据分治
}return clear(),0;
}
待到再迷茫时回头望,所有脚印会发出光芒