P4249 [WC2007]剪刀石头布

有一个竞赛图,要给一些边定向,求三元环最多的数量

反过来考虑最少的不是环的三个点(称为不好的环),一定有一个点有2条入边,一个点有2条出边,一个点1入边1出边

可以对每一个不好的环只记录入边为2的点,那么不好的环有\(\sum C_{deg_i}^2\)个,其中\(deg_i\)\(i\)的入度

因为\(C_{x}^2=x(x-1)/2\),差分后导数\(>0\),所以可以费用流,就做完了

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int a[101][101],num[101][101],cnt,o[101][101],S,T;
int fir[6000],dis[1000010],nxt[1000010],w[1000010],cost[100010],id=1;
il vd link(int a,int b,int c){
    nxt[++id]=fir[a],fir[a]=id,dis[id]=b,w[id]=1,cost[id]=c;
    nxt[++id]=fir[b],fir[b]=id,dis[id]=a,cost[id]=-c;
}
il bool Mincost(int&total){
    static int dist[6000],lst[6000],inq[6000],que[6000],hd,tl;
    hd=tl=0;memset(dist,63,4*(cnt+1));dist[S]=0;inq[S]=1;que[tl++]=S;
    while(hd^tl){
        int x=que[hd];
        for(int i=fir[x];i;i=nxt[i])
            if(w[i]&&dist[dis[i]]>dist[x]+cost[i]){
                dist[dis[i]]=dist[x]+cost[i];lst[dis[i]]=i;
                if(!inq[dis[i]]){
                    inq[dis[i]]=1,que[tl++]=dis[i];
                    if(tl==6000)tl=0;
                }
            }
        ++hd;if(hd==6000)hd=0;
        inq[x]=0;
    }
    if(dist[T]==dist[0])return 0;
    for(int i=lst[T];i;i=lst[dis[i^1]])w[i]=0,w[i^1]=1,total+=cost[i];
    return 1;
}
int main(){
    int n=gi();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            a[i][j]=gi();
    cnt=n;
    S=++cnt,T=++cnt;
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j){
            num[i][j]=++cnt;
            if(a[i][j]==0)link(S,i,0);
            else if(a[i][j]==1)link(S,j,0);
            else link(S,num[i][j],0),o[i][j]=id+1,link(num[i][j],i,0),link(num[i][j],j,0);
        }
    for(int i=1;i<=n;++i)
        for(int j=n;j;--j)
            link(i,T,j-1);
    int ans=0;while(Mincost(ans));
    printf("%d\n",n*(n-1)*(n-2)/6-ans);
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
            if(a[i][j]==2)a[i][j]=w[o[i][j]],a[j][i]=!a[i][j];
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j)printf("%d ",a[i][j]);
        puts("");
    }
    return 0;
}
posted @ 2018-11-30 09:21  菜狗xzz  阅读(171)  评论(3编辑  收藏  举报