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

来源:互联网 发布:淘宝卖家中心界面 编辑:程序博客网 时间:2024/05/19 16:37

传送门: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;}




0 0