[ABC220H] Security Camera 题解
Meet in the Middle+FWT
见过好几道 mitm 的题但当时都不是很懂,第一道明白的 mitm 的题
学习自 https://www.cnblogs.com/ak-dream/p/AK_Dream122.html
Statement
给定一个 个点 条边的无向图
每一个点可以删/不删,删去一个点同时会删去所有与他相连的边
计数有多少种删点的方法使得删边数为偶数。
H - Security Camera (atcoder.jp)
Solution
题目可以理解为导出子图边数为偶数的方案数
容易想到暴力做法, 表示选择点集 的删边奇偶性,可以在 做出来,然后 判断即可。
for(int s=1;s<(1<<n);++s){
int x=log2(lowbit(s))+1;
f[s]=f[s^lowbit(s)];
for(auto v:Edge[x])
f[s]^=(!(s>>(v-1)&1));
}
考虑 Meet in the Middle ,把前 个点化为 ,其他化入 集合
表示选择 删边奇偶性;
表示选择 删边 的奇偶性(即只删除两头都在 内的边);
表示一个点集,满足 ,且所有 中的点到集合 都有奇数条边
现在,题目转化成枚举 ,计数有多少个 满足
考虑枚举中间的 ,设
那么答案就是
这个式子已经很 FWT 了,我们现在只需要把它改写一下
设 ,
则 ,FWT 一下就可以了
算一算,总复杂度 ,很对。
Code
#include<bits/stdc++.h>
#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];
vector<int>Edge[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<n;j+=i)
for(int k=j;k<j+p;++k)
a[k]+=a[k+p]*op;
}
signed main(){
n=read(),m=read();
for(int i=1,u,v;i<=m;++i)
u=read(),v=read(),
Edge[u].push_back(v),
Edge[v].push_back(u);
int siz1=n/2,siz2=n-n/2;
for(int s=1;s<(1<<siz1);++s){
int x=log2(lowbit(s))+1,t=s^lowbit(s);
f1[s]=f1[t],g[s]=g[t];
for(auto v:Edge[x]){
if((v<=siz1&&(!((s>>(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<<siz2);++s){
int x=log2(lowbit(s))+1+siz1,t=s^lowbit(s);
f2[s]=f2[t],ppc[s]=ppc[t]+1;
for(auto v:Edge[x])
if(v>siz1&&!((s>>(v-siz1-1))&1))f2[s]^=1;
}
int ans=0,mx=(1<<siz1)-1;
for(int a=0;a<=1;++a)for(int b=0;b<=1;++b){
memset(c1,0,sizeof(c1)),memset(c2,0,sizeof(c2));
for(int s=0;s<(1<<siz1);++s)if(f1[s]==a)c1[g[mx^s]]++;//由于算的是到 s ,所以这里人为处理一下
for(int t=0;t<(1<<siz2);++t)if(f2[t]==b)c2[t]++;
fwt(c1,1<<siz2,1),fwt(c2,1<<siz2,1);
for(int i=0;i<(1<<siz2);++i)c1[i]*=c2[i];
fwt(c1,1<<siz2,-1);
for(int i=0;i<(1<<siz2);++i)
if(a^b^(ppc[i]&1)==0)ans+=c1[i];
}
printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】