Description

Input

Output
一个整数表示联盟里所有球队收益之和的最小值。
Sample Input
3 3
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1
Sample Output
43
orz huzecong
神犇的想法果然与众不同
首先看上去像网络流。这样就够了
先假设每场比赛两队都是输的,那么接下来转换的时候只要把其中一对变成赢的就好了。
考虑一支球队已经赢了x场输了y场现在要把输的y场之一变成赢的,考虑这样对答案的影响
原来是C[i]*x^2+D[i]*y^2,现在是C[i]*(x+1)^2+D[i]*(y-1)^2
那么在这支球队赢x场输y场的时候把一场输的变成赢的,对答案的贡献就是两个相减
对于一支一开始赢x场输y场的球队,我们向T连边,代价是从(x,y)变成(x+1,y-1)的代价。
然后还有从(x+1,y-1)到(x+2,y-2)……
因为是最小费用最大流,正确性显然
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<deque> #include<set> #include<map> #include<ctime> #define LL long long #define inf 0x3ffffff #define S 0 #define T n+m+1 #define N 6010 using namespace std; inline LL read() { LL x=0,f=1; char ch= getchar (); while (ch< '0' ||ch> '9' ){ if (ch== '-' )f=-1;ch= getchar ();} while (ch>= '0' &&ch<= '9' ){x=x*10+ch- '0' ;ch= getchar ();} return x*f; } struct edge{ int to,next,from,v,c;}e[10*N]; struct match{ int x,y;}M[100010]; int head[N],from[N],dist[N],q[N]; bool mrk[N]; int n,m,ans,cnt=1; int a[N],b[N],C[N],D[N]; inline void ins( int u, int v, int w, int c) { e[++cnt].to=v; e[cnt].v=w; e[cnt].c=c; e[cnt].from=u; e[cnt].next=head[u]; head[u]=cnt; } inline void insert( int u, int v, int w, int c) { ins(u,v,w,c); ins(v,u,0,-c); } inline bool spfa() { for ( int i=0;i<=T;i++)dist[i]=inf; int t=0,w=1; dist[S]=0;q[0]=S;mrk[S]=1; while (t!=w) { int now=q[t++]; if (t==6005)t=0; for ( int i=head[now];i;i=e[i].next) if (e[i].v&&dist[now]+e[i].c<dist[e[i].to]) { dist[e[i].to]=dist[now]+e[i].c; from[e[i].to]=i; if (!mrk[e[i].to]) { mrk[e[i].to]=1; q[w++]=e[i].to; if (w==6005)w=0; } } mrk[now]=0; } return dist[T]!=inf; } inline void mcf() { int x=inf; for ( int i=from[T];i;i=from[e[i].from]) x=min(x,e[i].v); for ( int i=from[T];i;i=from[e[i].from]) { e[i].v-=x; e[i^1].v+=x; ans+=x*e[i].c; } } int main() { n=read();m=read(); for ( int i=1;i<=n;i++) { a[i]=read(); b[i]=read(); C[i]=read(); D[i]=read(); } for ( int i=1;i<=m;i++) { M[i].x=read(); M[i].y=read(); b[M[i].x]++; b[M[i].y]++; insert(S,i,1,0); insert(i,m+M[i].x,1,0); insert(i,m+M[i].y,1,0); } for ( int i=1;i<=n;i++) ans+=a[i]*a[i]*C[i]+b[i]*b[i]*D[i]; for ( int i=1;i<=m;i++) { int x=M[i].x,y=M[i].y; insert(m+x,T,1,2*a[x]*C[x]-2*b[x]*D[x]+C[x]+D[x]); a[x]++;b[x]--; insert(m+y,T,1,2*a[y]*C[y]-2*b[y]*D[y]+C[y]+D[y]); a[y]++;b[y]--; } while (spfa())mcf(); printf ( "%d\n" ,ans); return 0; } |
S向所有比赛连边,所有比赛向两队连边。所有队伍
——by zhber,转载请注明来源
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术