题意:有
n
n
n个球队,如果一支球队胜场为
x
i
x_i
xi,负场为
y
i
y_i
yi,那么他们的奖金即为
C
i
×
x
i
2
+
D
i
×
y
i
2
C_i\times x_i^2+D_i\times y_i^2
Ci×xi2+Di×yi2。现在知道了这些球队现在的胜场和负场,以及一些不确定的比赛,问总奖金最少是多少。
那么考虑一个球队多赢了一把奖金的变化。
先假设这个球队输了所有能输的比赛,那么现在他们的胜场是
x
x
x,负场是
y
y
y,现在他们赢回来了一局,那么胜场就是
x
+
1
x+1
x+1,负场是
y
−
1
y-1
y−1。奖金的变化就是
(
x
+
1
)
2
×
C
i
+
(
y
−
1
)
2
×
D
i
−
x
2
×
C
i
−
y
2
×
D
i
=
(
2
×
x
+
1
)
×
C
i
−
(
2
×
y
−
1
)
×
D
i
(x+1)^2\times C_i+(y-1)^2\times D_i-x^2\times C_i-y^2\times D_i=(2\times x+1)\times C_i-(2\times y-1)\times D_i
(x+1)2×Ci+(y−1)2×Di−x2×Ci−y2×Di=(2×x+1)×Ci−(2×y−1)×Di
所以现在就可以这样建出边了:
- 把不确定的对局看做点,与源点相连,容量为1
- 把不确定的对局对应的点和对局双方连边
- 把所有的球队按照上述方式和源点连最多胜场数量的边。
c o d e : code: code:
#include <bits/stdc++.h>
#define regi register int
int n,m;
int S,T;
int ans;
int mincost;
int dis[101000];
int vis[101000];
int maybe[1000000];
//maybe表示这个队最多能再获胜几把
int a[1000000],b[1000000];
int c[1000000],d[1000000];
int head[1000000],tot=1;
std::queue<int>q;
struct edge{
int to;
int nxt;
int flow;
int cost;
}e[1000000];
void add(int x,int y,int flow,int cost){
e[++tot]={y,head[x],flow,cost};
head[x]=tot;
e[++tot]={x,head[y],0,-cost};
head[y]=tot;
}
bool spfa(){
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
q.push(S);
dis[S]=0;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(regi i=head[x];i;i=e[i].nxt){
regi y=e[i].to;
if(e[i].flow&&dis[y]>dis[x]+e[i].cost){
dis[y]=dis[x]+e[i].cost;
if(!vis[y]){
vis[y]=1;
q.push(y);
}
}
}
}
return dis[T]!=0x3f3f3f3f;
}
int dfs(int x,int min){
vis[x]=1;
if(x==T)
return min;
int flow=0;
for(regi i=head[x],w;i;i=e[i].nxt){
int y=e[i].to;
if((!vis[y]||y==T)&&e[i].flow>0&&dis[y]==dis[x]+e[i].cost)
if(w=dfs(y,std::min(min-flow,e[i].flow))){
mincost+=w*e[i].cost;
e[i].flow-=w;
e[i^1].flow+=w;
flow+=w;
}
}
return flow;
}
void dinic(){
while(spfa()){
vis[T]=1;
while(vis[T]){
memset(vis,0,sizeof vis);
dfs(S,0x3f3f3f3f);
}
}
}
main(){
scanf("%d%d",&n,&m);
S=1,T=2;
for(regi i=1;i<=n;++i)
scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
for(regi i=1,x,y;i<=m;++i){
scanf("%d%d",&x,&y);
maybe[x]++;
maybe[y]++;
b[x]++;
b[y]++;
//先假设他们全败了
add(S,i+2,1,0);
add(i+2,2+m+x,1,0);
add(i+2,2+m+y,1,0);
}
for(regi i=1;i<=n;++i){
ans+=a[i]*a[i]*c[i]+b[i]*b[i]*d[i];
//先计算所有团队能败的全败需要的资金
for(regi j=1;j<=maybe[i];++j){
add(2+m+i,T,1,(2*a[i]+1)*c[i]-(2*b[i]-1)*d[i]);
++a[i];
--b[i];
//记录现在的胜场和负场,按照之前的建边方式建边
}
}
dinic();
printf("%d\n",ans+mincost);
return 0;
}
/*代码实现:
记录每个球队最多能赢的胜场,然后建边。
*/