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