CF908H New Year and Boolean Bridges

发现若 (G_{i,j}=mathsf{A}),则 (i,j) 一定在同一个强连通分量中,而同一个强连通分量中不能存在 (mathsf{X}),因此先将满足 (G_{i,j}=mathsf{A})(i,j) 进行缩点,然后通过 (mathsf{X}) 判断是否无解。

得最优情况一定是每个强连通分量内部为一个环,强连通分量之间连成一条链。那么将强连通分量之间两两合并,就能减少链上边的使用,得答案为 (n-1) 加上点数 (geqslant 2) 的强连通分量个数。

接下来就只考虑 (mathsf{X}) 边即可,因为在连通图中,若 (mathsf{A})(mathsf{X}) 满足,(mathsf{O}) 就能满足。于是问题就转化为:将所有点划分到若干强连通分量中,且这些强连通分量间无连边,最小化强连通分量个数。

发现点数 (geqslant 2) 的强连通分量只有 (23),所以考虑状压 (DP)。设 (f(S)) 为点集 (S) 是否合法,那么其子集卷积 (k) 次后使得全集合法的最小的 (k) 即为所求。设强连通分量个数为 (cnt),其复杂度为 (O(cnt^32^{cnt})),无法接受。

发现若存在合法集合 (S,T),则 (S-S cup T) 也是合法集合,因此可以用或卷积来代替子集卷积,得复杂度为 (O(cnt^22^{cnt}))。发现并不用每次都做 (FWT),因为只需知道全集的情况即可,于是可以用公式:

[large f(S)=sum_{T subseteq S} (-1)^{operatorname{popcount}(S-T)}hat f(T) ]

(O(2^{cnt})) 单点求或卷积的值。于是复杂度就为 (O(cnt2^{cnt})) 了。

(f(S)) 时就每次向 (S-operatorname{lowbit}(S)) 中加入 (operatorname{lowbit}(S)) 即可,这里预处理出每个强连通分量和哪些强连通分量有 (mathsf{X}) 边就能快速转移。

#include<bits/stdc++.h>
#define maxn 55
#define maxs 8400010
#define lowbit(x) (x&(-x))
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,tot,all,ans;
int fa[maxn],id[maxn],siz[maxn],col[maxn],e[maxn];
int f[maxs],g[maxs],c[maxs],cnt[maxs];
bool vis[maxn];
char w[maxn][maxn];
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
bool check()
{
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(w[i][j]=='X'&&find(i)==find(j))
                return false;
    return true;
}
int get(int s)
{
    int v=0;
    while(s) s>>=1,v++;
    return v;
}
void FWT(int *a)
{
    for(int len=1;len<=all;len<<=1)
        for(int i=0;i<=all;i+=len<<1)
            for(int j=i;j<i+len;++j)
                a[j+len]+=a[j];
}
int main()
{
    read(n);
    for(int i=1;i<=n;++i) scanf("%s",w[i]+1),fa[i]=i;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(w[i][j]=='A')
                fa[find(i)]=find(j);
    if(!check())
    {
        puts("-1");
        return 0;
    }
    for(int i=1;i<=n;++i)
    {
        siz[col[i]=find(i)]++;
        if(vis[col[i]]) continue;
        vis[col[i]]=true;
    }
    for(int i=1;i<=n;++i)
        if(i==col[i]&&siz[i]!=1)
            id[i]=++tot;
    if(!tot)
    {
        printf("%d
",n-1);
        return 0;
    }
    all=(1<<tot)-1;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            if(w[i][j]!='X'||siz[col[i]]==1||siz[col[j]]==1) continue;
            int x=id[col[i]],y=id[col[j]];
            e[x]|=1<<(y-1),e[y]|=1<<(x-1);
        }
    }
    f[0]=1;
    for(int s=1;s<=all;++s)
    {
        int t=s^lowbit(s);
        if(!(e[get(lowbit(s))]&s)) f[s]=f[t];
    }
    if(f[all])
    {
        printf("%d
",n);
        return 0;
    }
    FWT(f);
    for(int i=0;i<=all;++i) g[i]=f[i];
    for(int i=1;i<=all;++i) cnt[i]=cnt[i-lowbit(i)]+1;
    for(int i=0;i<=all;++i) c[i]=cnt[i^all]&1?-1:1;
    ans=n;
    while(1)
    {
        ans++;
        for(int i=0;i<=all;++i) f[i]*=g[i];
        int v=0;
        for(int i=0;i<=all;++i) v+=f[i]*c[i];
        if(v) break;
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/13871321.html