AGC006F Blackout

Link
先转化模型:给定一个有向图,若图中存在((u,v),(v,w)),那么可以连((u,w)),求最后有多少边。
那么我们现在先把每个弱连通块分开讨论,显然不同弱连通块之间是互不影响的。
把这个连通块三染色((R ightarrow G ightarrow B)),如果无法染色,那么这个弱连通块会变成一个完全图。
如果只用了不到三种颜色,那么肯定无法继续操作。
如果成功三染色,那么所有(R ightarrow B,G ightarrow R,B ightarrow G)都会连上。
用并查集染色即可。

#include<cstdio>
#include<numeric>
using i64=long long;
const int N=300007;
int a[N],b[N],f[N],g[N],fa[N],s[N],t[N],vis[N];
int read(){int x;scanf("%d",&x);return x;}
int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
void merge(int x,int y){fa[find(x)]=find(y);}
int main()
{
    int n=read(),m=read();i64 ans=0;std::iota(fa+1,fa+n*3+1,1);
    for(int i=1;i<=m;++i) ++f[a[i]=read()],++g[b[i]=read()],merge(a[i],b[i]+n),merge(a[i]+n,b[i]+n+n),merge(a[i]+n+n,b[i]);
    for(int i=1;i<=n;++i) if(++s[find(i)],++t[find(i+n)],f[i]&&g[i]) vis[find(i)]=vis[find(i+n)]=vis[find(i+n+n)]=1;
    for(int i=1;i<=n*3;++i) if(find(i)==i&&vis[i]) ans+=1ll*s[i]*t[i];
    for(int i=1;i<=m;++i) ans+=!vis[find(a[i])];
    printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12463634.html