ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[ABC220H] Security Camera 题解

2022-04-18 19:03:14  阅读:193  来源: 互联网

标签:f1 f2 删边 int 题解 sum Security ABC220H oplus


Meet in the Middle+FWT

见过好几道 mitm 的题但当时都不是很懂,第一道明白的 mitm 的题

学习自 https://www.cnblogs.com/ak-dream/p/AK_Dream122.html

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<<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 ,把前 \(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<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;
}

标签:f1,f2,删边,int,题解,sum,Security,ABC220H,oplus
来源: https://www.cnblogs.com/wyb-sen/p/16161098.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有