1. 程式人生 > >AtCoder Grand Contest 006 F - Blackout

AtCoder Grand Contest 006 F - Blackout

sin 所在 long long || 如果 open int 雙向 mem

Description

\(n*n\) 的棋盤上給出 \(m\) 個黑點,若 \((x,y)\),\((y,z)\) 都是黑點,那麽 \((z,x)\) 也會變成黑點,求最後黑點的數量
題面

Solution

把點 \((x,y)\) 看作一條從 \(x\)\(y\) 的有向邊
我們分析性質:
如果存在一個自環,那麽這個點所在的連通塊就會變成一個完全圖
原因是和這個點有單向邊的點都會變成雙向邊,有雙向邊之後就會形成自環,那麽就可以一直重復這個過程,就變成了完全圖

我們想辦法判斷圖中有沒有自環,我們發現:對原圖進行三染色之後:
1.如果產生了矛盾,那麽就有自環,就會形成一個完全圖,這個連通塊的答案就是點數的平方

2.如果染色完成了,那麽算出產生的邊的個數和原圖邊的個數就行了

對於第二種情況,還需要一些性質:
首先如果 \(color[x]±1 \mod 3 =color[u]\)\(x,u\) 在同一連通塊內,則一定有邊存在
所以設 \(a[x]\) 表示顏色為 \(x\) 的點的數量,答案就是 \(a[1]*a[2]+a[2]*a[3]+a[1]*a[3]\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,head[N],nxt[N*2],to[N*2],num=0,c[N],E=0,a[4],dis[N*2];
bool flag=0,vis[N*2];
vector<int>S;
inline void link(int x,int y,int z){
    nxt[++num]=head[x];to[num]=y;head[x]=num;dis[num]=z;}
inline void dfs(int x){
    S.push_back(x);
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i],t=c[x]+dis[i];
        if(!vis[i])vis[i]=1,E++;
        if(!t)t=3;if(t==4)t=1;
        if(c[u]){
            if(c[u]!=t)flag=1;
        }
        else c[u]=t,dfs(u);
    }
}
int main(){
    freopen("pp.in","r",stdin);
    freopen("pp.out","w",stdout);
    int x,y;ll ans=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        link(x,y,1);link(y,x,-1);
    }
    for(int i=1;i<=n;i++){
        if(!c[i]){
            vector<int>().swap(S);c[i]=1;flag=0;E=0;
            dfs(i);
            memset(a,0,sizeof(a));
            for(int j=S.size()-1;j>=0;j--)a[c[S[j]]]++;
            if(flag)ans+=(ll)S.size()*S.size();
            else if(!a[1] || !a[2] || !a[3])ans+=E/2;
            else ans+=1ll*a[1]*a[2]+1ll*a[2]*a[3]+1ll*a[1]*a[3];
        }
    }
    cout<<ans<<endl;
    return 0;
}

AtCoder Grand Contest 006 F - Blackout