给出一个 (n) 个点的竞赛图,有 (m) 条边已经确定方向,求出最多可能的三元环个数。
(nle 50 , mle frac{n(n-1)}{2}) 。
没有想到的网络流题,我还是太菜了qaq
三个点构不成三元环的充分必要条件是存在一个点仅对于这三个点的出度为 (2) 。
假设第 (i) 个点的出度为 (d_i) ,通过容斥,三元环的个数为 (frac{n(n-1)(n-2)}{6} - sumlimits_{i=1}^n inom{d_i}{2}) 。
那么目标就转化为最小化 (sumlimits_{i=1}^n inom{d_i}{2}) ,考虑使用网络流,非固定的边可以新建一个点,源点向新点连一条流量为 (1) 的边,新点再向两端的点各连一条流量为 (1) 的边,这样流到第 (i) 个点的流量就是 (d_i) ,而 (inom{d_i}{2}) 可以通过差分展开成 (sumlimits_{j=1}^{d_i} (j-1)) ,那么可以对于每个点,向汇点连 (n) 条边,第 (j) 条边的流量为 (1) ,费用为 (j-1) ,跑一遍最小费用最大流就可以统计最终答案了。
其实挺套路,果然是自己菜(
不知道为什么我跑了一遍最大费用也过了
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+5,N=105,inf=1e9;
int n,m,num,S,T,Map[N][N];
int read(){
int x=0,y=1;char ch=getchar();
while(ch<'0'||ch>'9') y=(ch=='-')?-1:1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*y;
}
int tot=1,first[M];
struct Edge{ int nxt,to,w,c; }e[M<<1];
void add(int x,int y,int w,int c){
e[++tot]=(Edge){first[x],y,w,c};
first[x]=tot;
}
void addedge(int x,int y,int w,int c){ add(x,y,w,c),add(y,x,0,-c); }
queue<int> Q;
int dis[M],pre[M],minn[M];bool vis[M];
bool spfa(){
while(!Q.empty()) Q.pop();
for(int i=0;i<=num;i++) dis[i]=inf,vis[i]=0;
Q.push(S),dis[S]=0,minn[S]=inf,vis[S]=1;
while(!Q.empty()){
int u=Q.front();Q.pop();vis[u]=0;
for(int i=first[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].w,c=e[i].c;
if(w&&dis[v]>dis[u]+c){
pre[v]=i;dis[v]=dis[u]+c;
minn[v]=min(minn[u],w);
if(!vis[v]) vis[v]=1,Q.push(v);
}
}
}
return dis[T]<inf;
}
void EK(){
while(spfa()){
int u=T;
while(u!=S){
int i=pre[u];
e[i].w-=minn[T],e[i^1].w+=minn[T];
u=e[i^1].to;
}
}
}
void solve(){
n=read(),m=read();S=0,T=1,num=n+1;
for(int i=1;i<=m;i++){
int x=read(),y=read();
Map[x][y]=1,Map[y][x]=-1;
addedge(S,x+1,1,0);
}
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(Map[i][j]==0)
Map[i][j]=Map[j][i]=++num,addedge(S,num,1,0),addedge(num,i+1,1,0),addedge(num,j+1,1,0);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) addedge(i+1,T,1,j-1);
EK();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) printf("0");
else{
int x=Map[i][j],flag=0;
if(x==1) printf("1");
else if(x==-1) printf("0");
else{
for(int k=first[x];k;k=e[k].nxt){
int v=e[k].to;
if(v==i+1&&!e[k].w) flag=1;
}
printf("%d",flag);
}
}
}
printf("
");
}
}
signed main(){
solve();
}