[ABC220H] Security Camera 题解
Meet in the Middle+FWT
见过好几道 mitm 的题但当时都不是很懂,第一道明白的 mitm 的题
学习自
Statement
给定一个 \(n\) 个点 \(m\) 条边的无向图 \(n\le 40,m\le \frac {n(n-1)}2\)
每一个点可以删/不删,删去一个点同时会删去所有与他相连的边
计数有多少种删点的方法使得删边数为偶数。
H - Security Camera (atcoder.jp)
Solution
题目可以理解为导出子图边数为偶数的方案数
容易想到暴力做法,\(f[S]\) 表示选择点集 \(S\) 的删边奇偶性,可以在 \((n2^n)\) 做出来,然后 \(2^n\) 判断即可。
for(int s=1;s<(1<>(v-1)&1));
}
考虑 Meet in the Middle ,把前 \(n/2\) 个点化为 \(S\),其他化入 \(T\) 集合
\(f1[s]\) 表示选择 \(s\in S\) 删边奇偶性;
\(f2[t]\) 表示选择 \(t\in T\) 删边 \((u,v),u\in T,v\in T\) 的奇偶性(即只删除两头都在 \(T\) 内的边);
\(g[s]\) 表示一个点集,满足 \(s\in S,g[s]\in T\) ,且所有 \(g[s]\) 中的点到集合 \(S\backslash s\) 都有奇数条边
现在,题目转化成枚举 \(s\in S,t\in T\) ,计数有多少个 \(s,t\) 满足
\[f1[s]\oplus f2[t]\oplus (popcount(g[s]\&t)\&1) \]考虑枚举中间的 \(g[s]\&T\) ,设
\[h[p][0/1]=\sum_{g[s]\&t=p}[f1[s]\oplus f2[t]=0/1] \]那么答案就是 \(\sum h[p][popcount(p)\&1]\)
这个式子已经很 FWT 了,我们现在只需要把它改写一下
设 \(c1[s][0/1]=\sum_{g[p]=s}[f1[p]=0/1]\) ,\(c2[t][0/1]=[f2[t]=0/1]\)
则 \(h[p][a\oplus b]=\sum_{s\&t=p}c1[s][a]\times s2[t][b]\) ,FWT 一下就可以了
算一算,总复杂度 \(O(\frac n22^{\frac n2})\) ,很对。
Code
#include
#define int long long
#define lowbit(x) (-x&x)
using namespace std;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
int s=0,w=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
bool f1[1<<20|5],f2[1<<20|5];
int g[1<<20|5],ppc[1<<20|5];
int c1[1<<20|5],c2[1<<20|5];
vectorEdge[55];
int n,m;
void fwt(int *a,int n,int op){
for(int i=2;i<=n;i<<=1)
for(int p=i>>1,j=0;j>(v-1))&1)))||v>siz1)f1[s]^=1;
if(v>siz1)g[s]^=(1<<(v-siz1-1));//这里实际上计算的是到 s 而不是到 S\s
}
}
for(int s=1;s<(1<siz1&&!((s>>(v-siz1-1))&1))f2[s]^=1;
}
int ans=0,mx=(1<