bzoj 2597 石头剪刀布

利用补集转化建图。可以得到ans=C(n,3)-sigma(C(win[i],2))。具体的意思就是两个胜场会破坏一个三元环。
之后展开,注意sigma(win[i])=n*(n-1)/2,因为比赛场次是C(n,2)个,因为写成了n,一直wa

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

#define md
#define ll long long
#define inf (int) 1e9
#define eps 1e-8
#define N 100010
#define M 400010
using namespace std;

struct yts { int x,t,f,c,ne;} e[2*M];
int v[N],dis[N],g[110][110],q[N];
bool vis[N];
int num=1,n,S,T;
int ans=0;

void put(int x,int y,int f,int c)
{
num++; e[num].x=x; e[num].t=y; e[num].f=f; e[num].c=c;
e[num].ne=v[x]; v[x]=num;
}

void add(int x,int y,int f,int c)
{
put(x,y,f,c); put(y,x,0,-c);
}

void ycl()
{
int tot=n;
for (int i=1;i<=n;i++)
for (int j=1;j<i;j++)
g[i][j]=++tot;
S=++tot; T=++tot;
}

bool spfa()
{
memset(vis,0,sizeof(vis));
for (int i=1;i<=T;i++) dis[i]=inf;
int h=0,w=1,x,y; q[1]=T; dis[T]=0; vis[T]=1;
while (h!=w)
{
h++; if (h>T+2) h=1; x=q[h];
for (int i=v[x];i;i=e[i].ne)
{
y=e[i].t;
if (e[i^1].f&&dis[y]>dis[x]+e[i^1].c)
{
dis[y]=dis[x]+e[i^1].c;
if (!vis[y])
{
w++; if (w>T+2) w=1; q[w]=y;
vis[y]=1;
}
}
}
vis[x]=0;
}
return dis[S]!=inf;
}

int dfs(int x,int fl)
{
vis[x]=1;
if (x==T) return fl;
int now=0,used=0;
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (!vis[y]&&dis[y]==dis[x]-e[i].c&&e[i].f)
{
now=dfs(y,min(fl-used,e[i].f));
e[i].f-=now; e[i^1].f+=now; used+=now; ans+=now*e[i].c;
if (used==fl) break;
}
}
return used;
}

void zkw()
{
while (spfa())
{
do
{
memset(vis,0,sizeof(vis));
dfs(S,inf);
} while (vis[T]);
}
}
void outit()
{
for (int i=2;i<=num;i+=2) printf("%d %d %d %d\n",e[i].x,e[i].t,e[i].f,e[i].c);
}
int main()
{
int x;
scanf("%d",&n);
ycl();
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
scanf("%d",&x);
if (j<i)
{
if (x==1) add(g[i][j],i,1,0);
else if (x==0) add(g[i][j],j,1,0);
else { add(g[i][j],i,1,0); add(g[i][j],j,1,0); }
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=2*n+1;j+=2)
add(i,T,1,j);
for (int i=1;i<=n;i++)
for (int j=1;j<i;j++)
add(S,g[i][j],1,0);
zkw(); //outit();
for (int i=1;i<=n;i++)
for (int j=1;j<i;j++)
{
x=g[i][j];
for (int k=v[x];k;k=e[k].ne) if (e[k].t!=S&&!e[k].f) g[i][j]=(e[k].t==i);
g[j][i]=g[i][j]^1;
}
for (int i=1;i<=n;i++) g[i][i]=0;
printf("%d\n",n*(n-1)*(n-2)/6+n*(n-1)/4-ans/2);
for (int i=1;i<=n;i++){
printf("%d",g[i][1]);
for (int j=2;j<=n;j++)
printf(" %d",g[i][j]);
printf("\n");}
return 0;
}


你可能感兴趣的:(bzoj 2597 石头剪刀布)