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; }