bzoj 2597 剪刀石头布 —— 拆边费用流

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2597

不合法的三个人之间的关系就是一个人赢了两次;

记 \( deg[i] \) 表示第 \( i \) 个人赢的次数,那么答案就是 \( C_{n}^{3} - \sum\limits_{i=1}^{n} C_{deg[i]}^{2} \)

对于不确定的关系,如果确定 \( x \) 赢,那么答案中减去的不合法情况又会多 \( deg[x] \) 种;

也就是第一次多 \( deg[x] \),第二次多 \( deg[x] + 1 \),第三次多 \( deg[x] + 2 \) ...

如果把这些作为费用,因为每次走费用最小的边,正好是第一次 \( deg[x] \),第二次 \( deg[x] + 1 \)...

于是把每条无向边作为一个点,提供1的流量给两个端点中的一个表示哪个赢,赢的端点 \( x \) 向汇点连一系列费用从 \( deg[x] \) 开始递增的边即可;

WA了一次还以为是写法太麻烦,正准备大改,突然发现输出格式不对...数字中间要加空格的。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int const xn=15005,xm=1e5+5,xxn=105,inf=0x3f3f3f3f;
int n,cnt,id[xxn][xxn],dis[xn],hd[xn],to[xm],nxt[xm],ct=1,c[xm],w[xm];
int S,T,pre[xn],inc[xn],deg[xxn],num[xxn],sid[xxn][xxn];
bool vis[xn];
queue<int>q;
struct N{int u,v;}ed[xm];
int rd()
{
  int ret=0,f=1; char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return f?ret:-ret;
}
void add(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
bool bfs()
{
  memset(dis,0x3f,sizeof dis);
  memset(pre,0,sizeof pre);
  memset(inc,0x3f,sizeof inc);
  dis[S]=0; q.push(S); vis[S]=1;
  while(q.size())
    {
      int x=q.front(); q.pop(); vis[x]=0;
      for(int i=hd[x],u;i;i=nxt[i])
    if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
      {
        dis[u]=dis[x]+w[i];
        pre[u]=i; inc[u]=min(inc[x],c[i]);
        if(!vis[u])vis[u]=1,q.push(u);
      }
    }
  return dis[T]!=inf;
}
void up()
{
  int x=T;
  while(x!=S)
    {
      int i=pre[x];
      c[i]-=inc[T]; c[i^1]+=inc[T];
      x=to[i^1];
    }
}
int main()
{
  n=rd();
  for(int i=1;i<=n;i++)
    for(int j=1,d;j<=n;j++)
      {
    d=rd(); sid[i][j]=d; if(i>=j)continue;
    if(d==1)deg[i]++; else if(d==0) deg[j]++;
    else 
      {
        id[i][j]=id[j][i]=++cnt;
        ed[cnt].u=i,ed[cnt].v=j;
        num[i]++; num[j]++;//possible win
      }
      }
  S=0; T=cnt+n+1;
  for(int i=1;i<=cnt;i++)
    {
      add(S,i,0,1),add(i,S,0,0);
      add(i,ed[i].u+cnt,0,1),add(ed[i].u+cnt,i,0,0);//+cnt
      add(i,ed[i].v+cnt,0,1),add(ed[i].v+cnt,i,0,0);
    }
  int ans=0;
  for(int i=1;i<=n;i++)ans+=deg[i]*(deg[i]-1)/2;
  for(int i=cnt+1;i<=cnt+n;i++)
    for(int j=1;j<=num[i-cnt];j++)//-cnt
      add(i,T,deg[i-cnt]+j-1,1),add(T,i,0,0);//-cnt
  while(bfs())ans+=dis[T]*inc[T],up();//*
  printf("%d\n",n*(n-1)*(n-2)/6-ans);
  for(int i=1;i<=n;i++,puts(""))
    for(int j=1;j<=n;j++)
      {
    if(sid[i][j]!=2){printf("%d ",sid[i][j]); continue;}
    int x=id[i][j],t;
    for(int k=hd[x],u;k;k=nxt[k])
        if(to[k]==i+cnt&&!c[k]){t=i; break;}//+cnt
        else if(to[k]==j+cnt&&!c[k]){t=j; break;}
    if(t==i)printf("1 "),sid[j][i]=0;
    else printf("0 "),sid[j][i]=1;
      }
  return 0;
}

 

posted @ 2018-12-14 10:20  Zinn  阅读(162)  评论(0编辑  收藏  举报