• 洛谷 P4307 [JSOI2009]球队收益 / 球队预算(最小费用最大流)


    题面

    luogu

    题解

    最小费用最大流

    先假设剩下(m)场比赛,双方全输。
    考虑(i)赢一局的贡献
    (C_i*(a_i+1)^2+D_i*(b_i-1)^2-C_i*a_i^2-D_i*b_i^2)
    (=C _i+2*a_i*C_i+D_i-2*b_i*D_i)

    (m)个点限制每场比赛只有一个人赢,自(S)连一条((1, 0))的边

    然后从这(m)的点连向和比赛有关的两个点一条((1, 0))的边

    考虑关于(t)的边

    因为(a_i)(b_i)会变,不能直接连一条边

    然后我们观察一下上面那个式子,
    显然,赢得更多,贡献就增长越大

    那么,拆边,我们可以对于每个点连向(t)很多条边,容量为(1),价值是赢了对应场数的贡献

    贪心地想,因为赢得更多,贡献增长越大,要最小费用,我们会先走赢得少的边
    这样累加答案刚好满足条件呢

    Code

    #include<bits/stdc++.h>
    
    #define LL long long
    #define RG register
    
    using namespace std;
    template<class T> inline void read(T &x) {
    	x = 0; RG char c = getchar(); bool f = 0;
    	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    	x = f ? -x : x;
    	return ;
    }
    template<class T> inline void write(T x) {
    	if (!x) {putchar(48);return ;}
    	if (x < 0) x = -x, putchar('-');
    	int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    	for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
    }
    
    const int N = 10000, inf = 2147483647;
    
    struct node {
    	int to, nxt, w, v;
    }g[200*N];
    int last[N], gl = 1;
    void add(int x, int y, int w, int v) {
    	g[++gl] = (node) {y, last[x], w, v};
    	last[x] = gl;
    	g[++gl] = (node) {x, last[y], 0, -v};
    	last[y] = gl;
    }
    
    queue<int> q;
    int pre[N], dis[N], from[N], s, t;
    bool vis[N];
    
    bool spfa() {
    	memset(dis, 127, sizeof(dis));
    	dis[s] = 0;
    	q.push(s);
    	while (!q.empty()) {
    		int u = q.front(); q.pop();
    		for (int i = last[u]; i; i = g[i].nxt) {
    			int v = g[i].to;
    			if (g[i].w && dis[v] > dis[u] + g[i].v) {
    				dis[v] = dis[u] + g[i].v; pre[v] = u; from[v] = i;
    				if (!vis[v]) {
    					vis[v] = 1;
    					q.push(v);
    				}
    			}
    		}
    		vis[u] = 0;
    	}
    	return dis[t] != dis[0];
    }
    
    int Mcmf() {
    	int ans = 0;
    	while (spfa()) {
    		int di = inf;
    		for (int i = t; i != s; i = pre[i]) di = min(di, g[from[i]].w);
    		ans += di*dis[t];
    		for (int i = t; i != s; i = pre[i]) g[from[i]].w -= di, g[from[i]^1].w += di;
    	}
    	return ans;
    }
    int a[N], b[N], c[N], d[N], cnt[N];
    
    int main() {
    	int n, m, sum = 0;
    	read(n), read(m);
    	s = m+n+1, t = s+1;
    	for (int i = 1; i <= n; i++)
    		read(a[i]), read(b[i]), read(c[i]), read(d[i]);
    	for (int i = 1; i <= m; i++) {
    		int x, y;
    		read(x), read(y);
    		b[x]++, b[y]++;
    		cnt[x]++; cnt[y]++;
    		add(s, i, 1, 0);
    		add(i, x+m, 1, 0);
    		add(i, y+m, 1, 0);
    	}
    	for (int i = 1; i <= n; i++)
    		sum += c[i]*a[i]*a[i] + d[i]*b[i]*b[i];
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j <= cnt[i]; j++) {
    			add(i+m, t, 1, c[i]+2*a[i]*c[i]+d[i]-2*b[i]*d[i]);
    			b[i]--; a[i]++;
    		}
    	}
    	printf("%d
    ", sum+Mcmf());
    	return 0;
    }
    
    
  • 相关阅读:
    tomcat-jvm参数优化
    k8s集群命令用法
    Zabbix-配置QQ邮箱报警通知
    zabbix监控实现原理
    adb无线调试安卓
    tiddlywiki安装和入门
    python处理excel和word脚本笔记
    路由和交换机调试笔记
    linux常用命令
    进程和线程的代码实现
  • 原文地址:https://www.cnblogs.com/zzy2005/p/10299119.html
Copyright © 2020-2023  润新知