把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷7670】[JOI2018] Snake Escaping(数据分治)

题目链接

  • 给定 \(a_{0\sim 2^n-1}\)\(q\) 次询问,每次给出一个由 0,1,? 构成的长度为 \(n\) 的字符串,其中 ? 可以替换为 01,求能得到的所有 \(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;
}
posted @ 2021-11-23 20:52  TheLostWeak  阅读(17)  评论(0编辑  收藏  举报