题目链接
题目解法
考虑简化问题
若
(
x
,
y
)
(x,y)
(x,y) 是黑点,则从
x
−
>
y
x->y
x−>y 连一条边
这样将问题转化成了对于
x
−
>
y
−
>
z
x->y->z
x−>y−>z 的路径,必须连
z
−
>
x
z->x
z−>x,答案就是边的个数
我们可以把
x
−
>
y
x->y
x−>y 类似成无向边,然后对于无向图的连通块,三染色
给出结论:
-
若可以三染色,且颜色个数 < 3 <3 <3,则不会新增边
证明:若会新增边,则必有 x − > y − > z x->y->z x−>y−>z 的路径,那么一定会染 3 种颜色 -
若可以三染色,且颜色个数 = 3 =3 =3,令颜色 x x x 有 s x s_x sx 个,则最终边数为 s 0 s 1 + s 1 s 2 + s 2 s 0 s_0s_1+s_1s_2+s_2s_0 s0s1+s1s2+s2s0
证明:考虑数学归纳法
若当前图是满足结论的,考虑新增加一个与当前图连通的点 y y y,那么必有当前图内的一点 x x x 有 x − > y x->y x−>y 的边(即使是反向边只要设权为 -1 即可,这里只考虑正向边)
若 y y y 的颜色为 1,则 x x x 的颜色为 0
有 z − > x − > y z->x->y z−>x−>y, z z z 的颜色为 2,所以 y y y 必定连向所有颜色为 2 的点,所以颜色 1 − > 2 1->2 1−>2 得证
同理有 x − > y − > z x->y->z x−>y−>z 的边,所以 0 − > 1 0->1 0−>1 得证
2 − > 0 2->0 2−>0 与 x x x 无关,所以得证 -
若无法三染色,则最终状态是完全图
令点数为 n n n,则最终边数为 n 2 n^2 n2
证明:首先所有点都会有一条连向自己的边,证明的话考虑无法三染色,必有 x − > y x->y x−>y,且 x , y x,y x,y 颜色相同
上面的结论就易证所有点都会互相连边
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=100100;
int n,m,col[N],nodes,lines,cnt[3];
int e[N<<1],h[N],ne[N<<1],w[N<<1],idx;
bool flg,vis[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void add(int x,int y,int z){ e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;}
void dfs(int u){
vis[u]=1,nodes++,cnt[col[u]]++;
for(int i=h[u];~i;i=ne[i]){
int v=e[i],necol=(col[u]+w[i]+3)%3;
lines++;
if(vis[v]){
if(col[v]!=necol) flg=1;
continue;
}
col[v]=necol;
dfs(v);
}
}
signed main(){
n=read(),m=read();
memset(h,-1,sizeof(h));
for(int i=1;i<=m;i++){
int x=read(),y=read();
add(x,y,1),add(y,x,-1);
}
int ans=0;
for(int i=1;i<=n;i++) if(!vis[i]){
cnt[0]=cnt[1]=cnt[2]=0;
flg=nodes=lines=0,dfs(i);
// cout<<flg<<' '<<nodes<<' '<<lines<<'\n';
if(flg) ans+=nodes*nodes;
else if((cnt[0]>0)+(cnt[1]>0)+(cnt[2]>0)<3) ans+=lines/2;
else ans+=cnt[0]*cnt[1]+cnt[1]*cnt[2]+cnt[2]*cnt[0];
}
printf("%lld",ans);
return 0;
}