第一眼就觉得是费用流,但是建图确实很麻烦,逐渐递增这个费用到时想到了
solution:
首先,对于每一个事件,我们从s向这个事件连一条容量为1的边,然后对于这个事件,向两个点连一条容量为1的边。
这样满足了事件的关系。可以发现,我们的容量还是由s->事件的1来控制,只是有两个点的选择,即哪个点胜利。
第二,对于每个点,我们让它向t连cnt(在事件中出现的次数)条边,每次都去增长这个费用。
所以我们应该从最小开始,即我们一开始假定这个点在出现的事件中都输。
假定当前赢了a局,输了b局
那么由c*(a+1)^2+d*(b-1)^2*d - (a^2*c+b^2*d) = (2*a+1)*c - (2*b-1)*d)
可以发现,每多赢一次,费用都是以当前的a,b为基准来增加上面这么多费用。
所以我们每次连边,都连容量为1,费用为较上一次增长后的费用
那么,为什么不能连容量1,2,3,4,然后费用较原始代价增长的边呢。
因为这样可能经过同时1,2的边,然后代价对于第一次的计算了两次,会出现不合法的更优情况,所以不行。
// Author: levil #pragma GCC optimize(2) #pragma GCC optimize(3) #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<int,int> pii; const int N = 1e4; const int M = 1e4; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e9 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline int read(){ int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } void print(int x){ if(x < 0){x = -x;putchar('-');} if(x > 9) print(x/10); putchar(x%10+'0'); } } using namespace FASTIO; void FRE(){ freopen("data1.in","r",stdin); freopen("date1.out","w",stdout); } int n,m,s,t,cnt = -1,maxflow = 0,mincost = 0; int head[N],pre[N],cal[N],dis[N],vis[N];//pre记录前驱,cal记录最短增广路上的最小流量,dis记录最短路 struct Node{int to,dis,flow,next;}e[M<<1]; inline void add(int u,int v,int w,int flow)//费用才是距离,流量是容量 { e[++cnt].to = v,e[cnt].dis = w,e[cnt].flow = flow,e[cnt].next = head[u],head[u] = cnt; e[++cnt].to = u,e[cnt].dis = -w,e[cnt].flow = 0,e[cnt].next = head[v],head[v] = cnt; } bool spfa() { memset(vis,0,sizeof(vis)); for(int i = 0;i <= t;++i) dis[i] = INF; queue<int> Q; Q.push(s); dis[s] = 0,vis[s] = 1,cal[s] = INF; while(!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = 0;//spfa标准操作,清除标记 for(int i=head[u];i!=-1;i=e[i].next) { int v = e[i].to,d = e[i].dis,flow = e[i].flow; if(flow <= 0) continue;//没有剩余流量可流 if(dis[v] > dis[u]+d) { dis[v] = dis[u]+d; cal[v] = min(cal[u],flow);//更新增广路上的最小流量 pre[v] = i;//前驱记录i,因为是链式前向星 if(!vis[v]) vis[v] = 1,Q.push(v); } } } if(dis[t] == INF) return false; return true; } void MCMF() { while(spfa()) { int x = t; maxflow += cal[t]; mincost += dis[t]*cal[t]; while(x!=s) { int i = pre[x]; e[i].flow -= cal[t]; e[i^1].flow += cal[t]; x = e[i^1].to; } } } int A[N],B[N],C[N],D[N],sum[N]; int main() { memset(head,-1,sizeof(head)); n = read(),m = read(); for(rg int i = 1;i <= n;++i) A[i] = read(),B[i] = read(),C[i] = read(),D[i] = read(); s = 0,t = n+m+1;//t放在n,m输入下面。。 for(rg int i = 1;i <= m;++i) { int x,y;x = read(),y = read(); B[x]++,B[y]++; sum[x]++,sum[y]++; add(s,i,0,1); add(i,x+m,0,1); add(i,y+m,0,1); } LL ans = 0; for(rg int i = 1;i <= n;++i) ans += C[i]*A[i]*A[i]+D[i]*B[i]*B[i]; for(rg int i = 1;i <= n;++i) { for(rg int j = 1;j <= sum[i];++j) { add(i+m,t,(2*A[i]+1)*C[i]-(2*B[i]-1)*D[i],1); A[i]++,B[i]--; } } MCMF(); printf("%lld\n",ans+mincost); //system("pause"); return 0; }