bzoj1449: [JSOI2009]球队收益&&bzoj2597: [Wc2007]剪刀石头布

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1449

http://www.lydsy.com/JudgeOnline/problem.php?id=2597

思路:首先是球队收益,有输有赢不好处理

先假设后面所有比赛都是输,算出收益

然后计算未进行的比赛会带来多少收益


首先从S向每场比赛连边,容量为1,表示比赛只能有一个队赢,费用为0

每场比赛向比赛的两个队连边,容量为1,费用为0

关键是怎么计算每个队增加的收益


拆边,把每多赢一场增加的收益作为费用,容量为1

现在一个队已经赢了win[i]场,输了lose[i]+rem[i](剩下未比赛的场数)场

现在它的收益为C[i]*win[i]^2+D[i]*(lose[i]+rem[i])^2

那么它如果多赢了一场

收益为C[i]*(win[i]+1)^2+D[i]*(lose[i]+rem[i]-1)^2

做差可得2*C[i]*win[i]-2*D[i]*(lose[i]+rem[i])+C[i]+D[i]

这就是它在剩余的比赛中赢第一场的收益


同样,它在剩余比赛中赢的第j场的收益为

2*C[i]*(win[i]+j)-2*D[i]*(lose[i]+rem[i]-j)+C[i]+D[i]

这个是单调增的,也只有单调增时才能拆边

因为我们这时不会先得到后一场的收益,再得到前一场的收益


所以从每个队向T连0-rem[i]这rem[i]+1条边

每条边的费用就是这场比赛能带来的收益


剪刀石头布就难想一些

首先是补集转化

总共有C(n,3)个三元组


一个三元组中,如果有一个点赢了两场,那么就不是“剪刀石头布

所以答案就是C(n,3)-ΣC(win[i],2)

展开:C(n,3)-(win[i])*(win[i]-1)/2

同样建图,展开,发现后面部分每赢一场能得到的收益为增,这里是求最小费用,所以也一定会按场次顺序选择边流过


bzoj1449:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=10010,maxm=200010,inf=1061109567;
using namespace std;
int n,m,win[maxn],los[maxn],C[maxn],D[maxn],rem[maxn],mincost,maxflow,sum;
struct data{int x,y;}G[maxn];
inline int game(int x){return x;}
inline int team(int x){return m+x;}

struct zkw_flow{
	int pre[maxm],now[maxn],son[maxm],val[maxm],cost[maxm],tot,dis[maxn],S,T,q[maxn+10],head,tail;bool bo[maxn];
	void add(int a,int b,int c,int d){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c,cost[tot]=d;}
	void ins(int a,int b,int c,int d){add(a,b,c,d),add(b,a,0,-d);}
	void init(){memset(now,0,sizeof(now)),tot=1;}
	void spfa(){
		memset(dis,63,sizeof(dis));
		memset(bo,0,sizeof(bo));
		q[tail=1]=S,dis[S]=head=0,bo[S]=1;
		while (head!=tail){
			if (++head>maxn) head=1;
			int x=q[head];
			for (int y=now[x];y;y=pre[y]){
				if (val[y]>0&&dis[son[y]]>dis[x]+cost[y]){
					dis[son[y]]=dis[x]+cost[y];
					if (!bo[son[y]]){
						if (++tail>maxn) tail=1;
						q[tail]=son[y],bo[son[y]]=1;
					}
				}
			}
			bo[x]=0;
		}
	}
	bool relabel(){
		int mins=inf;
		for (int i=S;i<=T;i++) if (bo[i])
			for (int y=now[i];y;y=pre[y]) if (!bo[son[y]]&&val[y])
				mins=min(mins,dis[i]+cost[y]-dis[son[y]]);
		if (mins==inf) return 0;
		for (int i=S;i<=T;i++) if (!bo[i]) dis[i]+=mins;
		return 1;
	}
	int find(int x,int low,int cos){
		if (x==T){mincost+=low*cos;return low;}
		bo[x]=1;int res=0;
		for (int y=now[x];y&&low;y=pre[y]){
			if (!val[y]||bo[son[y]]||dis[son[y]]!=dis[x]+cost[y]) continue;
			int tmp=find(son[y],min(low,val[y]),cos+cost[y]);
			res+=tmp,low-=tmp,val[y]-=tmp,val[y^1]+=tmp;
		}
		return res;
	}
	void work(){
		spfa();int aug=0;
		do{
			do{
				memset(bo,0,sizeof(bo));
				aug=find(S,inf,0),maxflow+=aug;
			}while (aug);
		}while (relabel());
	}
}F;

int main(){
	scanf("%d%d",&n,&m),F.S=0,F.T=m+n+1;F.init();
	for (int i=1;i<=n;i++) scanf("%d%d%d%d",&win[i],&los[i],&C[i],&D[i]);
	for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),G[i]=(data){x,y},rem[x]++,rem[y]++;
	for (int i=1;i<=n;i++) sum+=win[i]*win[i]*C[i]+(los[i]+rem[i])*(los[i]+rem[i])*D[i];
	for (int i=1;i<=m;i++) F.ins(F.S,game(i),1,0),F.ins(game(i),team(G[i].x),1,0),F.ins(game(i),team(G[i].y),1,0);
	for (int i=1;i<=n;i++)
		for (int j=0;j<=rem[i];j++)
			F.ins(team(i),F.T,1,2*(win[i]+j)*C[i]-2*(los[i]+rem[i]-j)*D[i]+C[i]+D[i]);
	F.work(),printf("%d\n",sum+mincost);
	return 0;
<span style="font-size:14px;">}</span>

bzoj2597

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=15010,maxm=60010,inf=1061109567;
using namespace std;
int n,g[105][105],win[105],sum,maxflow,totcost;
inline int team(int x){return x+n*n;}
inline int game(int x,int y){return (x-1)*n+y;}

struct zkw_flow{
	int pre[maxm],now[maxn],son[maxm],val[maxm],cost[maxm],dis[maxn],tot,S,T;bool bo[maxn];
	void add(int a,int b,int c,int d){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c,cost[tot]=d;}
	void ins(int a,int b,int c,int d){add(a,b,c,d),add(b,a,0,-d);}
	void init(){memset(now,0,sizeof(now)),tot=1;}
	bool relabel(){
		int mins=inf;
		for (int i=S;i<=T;i++) if (bo[i])
			for (int y=now[i];y;y=pre[y]) if (val[y]&&!bo[son[y]])
				mins=min(mins,dis[i]+cost[y]-dis[son[y]]);
		if (mins==inf) return 0;
		for (int i=S;i<=T;i++) if (!bo[i]) dis[i]+=mins;
		return 1;
	}
	int find(int x,int low,int cos){
		if (x==T){totcost+=cos*low;return low;}
		bo[x]=1;int res=0;
		for (int y=now[x];y;y=pre[y]){
			if (bo[son[y]]||!val[y]||dis[x]+cost[y]!=dis[son[y]]) continue;
			int tmp=find(son[y],min(low,val[y]),cos+cost[y]);
			res+=tmp,low-=tmp,val[y]-=tmp,val[y^1]+=tmp;
			if (!low) break;
		}
		return res;
	}
	void work(){
		int aug=0;memset(dis,0,sizeof(dis)),maxflow=totcost=0;
		do{
			do{
				memset(bo,0,sizeof(bo));
				aug=find(S,inf,0),maxflow+=aug;
			}while (aug);
		}while (relabel());
	}
	void print(){
		for (int i=game(1,1);i<=game(n,n);i++) if (now[i]){
			int y1=now[i],y2=pre[y1],v1=son[y1]-n*n,v2=son[y2]-n*n;
			if (!val[y1]) g[v1][v2]=1,g[v2][v1]=0;
			else g[v2][v1]=1,g[v1][v2]=0;
		}
	}
}F;

int main(){
	scanf("%d",&n),F.init(),F.S=0,F.T=team(n)+1;
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&g[i][j]),win[i]+=(g[i][j]==1);
	for (int i=1;i<=n;i++) sum+=win[i]*(win[i]-1)/2;
	for (int i=1;i<=n;i++) for (int j=win[i];j<=n;j++) F.ins(team(i),F.T,1,j);
	for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) if (g[i][j]==2)
		F.ins(F.S,game(i,j),1,0),F.ins(game(i,j),team(i),1,0),F.ins(game(i,j),team(j),1,0);
	F.work(),F.print();
	printf("%d\n",n*(n-1)*(n-2)/6-sum-totcost);
	for (int i=1;i<=n;i++,puts("")) for (int j=1;j<=n;j++) printf("%d ",g[i][j]);
	return 0;
}




你可能感兴趣的:(bzoj1449: [JSOI2009]球队收益&&bzoj2597: [Wc2007]剪刀石头布)