• Luogu P1646 【[国家集训队]happiness】


    Description

    传送门


    Solution

    一种列方程的套路。

    我们先单独找出两个点的关系来考虑。

    假设有(x)(y)两个同学,向(S)连边代表选理科,向(T)连边代表选文科。设(S)(x)连的有向边为(a)(S)(y)连的有向边为(b)(x)(T)连的有向边为(c)(y)(T)连的有向边为(d)(x)(y)之间连了一条双向边(e),割掉一条边的流量代表损失了这么多的愉悦值。

    设都选理科的愉悦值是(v_1),都选文科的愉悦值是(v_2),按照题意可列方程组如下:

    [a + b = v_1 ]

    这是对应的两个同学都选理科。

    [c + d = v_2 ]

    这是对应的两个同学都选文科。

    [a + e + d = v_1 + v_2 ]

    这是对应的(x)选理科,(y)选文科。

    [b + e + c = v_1 + v_2 ]

    这是对应的(x)选文科,(y)选理科。

    解得

    [a = b = frac{v_1}{2}, c = d = frac{v_2}{2}, e = frac{v_1 + v_2}{2} ]

    那么我们只需要从(S)向每个点连一条容量为该同学选理科的愉悦值和他所有相邻的同学选理科的之和的有向边,从每个同学向(T)连一条容量为该同学选文科和他所有相邻的同学都选文科的愉悦值之和的有向边。

    每个同学向它相邻的同学连一条容量为他们同时都选理科或者文科的愉悦值的平均值的双向边。

    那么答案就是所有愉悦值的和减去最小割。

    注意因为要算平均值,所以可能出现小数,这样在一开始连边的时候乘二,最后再除以二就行了。


    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue> 
    using namespace std;
    
    const int N = 1000050;
    const int M = 2000050;
    const int INF = 999999999;
    
    int head[N], num = 1, n, m, s, t, tu[500][500][2], tmp[500][500], sum, d[N], x;
    
    struct Node
    {
    	int next, to, flow;
    } edge[M * 2];
    
    void Addedge(int u, int v, int w)
    {
    	edge[++num] = (Node){head[u], v, w};
    	head[u] = num;
    	return;
    }
    
    void Add(int u, int v, int w)
    {
    	Addedge(u, v, w);
    	Addedge(v, u, 0);
    	return;
    }
    
    int Id(int a, int b)
    {
    	return (a - 1) * m + b;
    } 
    
    template <class T>
    void Read(T &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) +st - '0', st = getchar();	
    	x = p ? -x : x;
    	return;
    } 
    
    template <class T>
    void Put(T x)
    {
    	if (x < 0) putchar('-'), x = -x;
    	if (x > 9) Put(x / 10);
    	putchar(x % 10 + '0');
    	return; 
    }
    
    bool Bfs()
    {
        queue<int> q;
        for (int i = 0; i <= t; i++) d[i] = 0;
        d[s] = 1; q.push(s);
        while (!q.empty())
        {
            int u = q.front(); q.pop();
            for (int i = head[u]; i; i = edge[i].next)
                if (!d[edge[i].to] && edge[i].flow)
                {
                    d[edge[i].to] = d[u] + 1;
                    q.push(edge[i].to);
                    if (edge[i].to == t) return 1;
                }
        }
        return 0;
    }
    
    int Dinic(int x, int flow)
    {
        if (x == t || !flow) return flow;
        int rest = flow;
        for (int i = head[x]; i && rest; i = edge[i].next)
            if (edge[i].flow && d[edge[i].to] == d[x] + 1)
            {
                int v = edge[i].to;
                int tmp = Dinic(v, min(rest, edge[i].flow));
                rest -= tmp;
                edge[i].flow -= tmp;
                edge[i ^ 1].flow += tmp;
                if (!tmp) d[v] = 0;
            }
        return flow - rest;
    }
    
    int Maxflow()
    {
    	int maxflow = 0, tmp;
    	while (Bfs())
    	{
    		tmp = Dinic(s, INF);
    		if (tmp) maxflow += tmp;
    	}
    	return maxflow;
    }
    
    int main()
    {
    	Read(n); Read(m); t = n * m + 1;
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m; j++)
    		{
    			Read(x);
    			sum += x;
    			tu[i][j][1] += x * 2;
    		}
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m; j++)
    		{
    			Read(x);
    			sum += x;
    			tu[i][j][0] += x * 2;
    		}
    	for (int i = 1; i <= n - 1; i++)
    		for (int j = 1; j <= m; j++)
    		{
    			Read(x);
    			sum += x;
    			tmp[i][j] += x;
    			tu[i][j][1] += x;
    			tu[i + 1][j][1] += x;
    		}
    	for (int i = 1; i <= n - 1; i++)
    		for (int j = 1; j <= m; j++)
    		{
    			Read(x);
    			sum += x;
    			tmp[i][j] += x;
    			Add(Id(i, j), Id(i + 1, j), tmp[i][j]);
    			Add(Id(i + 1, j), Id(i, j), tmp[i][j]);
    			tu[i][j][0] += x;
    			tu[i + 1][j][0] += x;
    			tmp[i][j] = 0;
    		}
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m - 1; j++)
    		{
    			Read(x);
    			sum += x;
    			tmp[i][j] += x;
    			tu[i][j][1] += x;
    			tu[i][j + 1][1] += x; 
    		}
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m - 1; j++)
    		{
    			Read(x);
    			sum += x;
    			tmp[i][j] += x;
    			Add(Id(i, j), Id(i, j + 1), tmp[i][j]);
    			Add(Id(i, j + 1), Id(i, j), tmp[i][j]);
    			tu[i][j][0] += x;
    			tu[i][j + 1][0] += x;
    		}
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m; j++)
    		{
    			Add(s, Id(i, j), tu[i][j][0]);
    			Add(Id(i, j), t, tu[i][j][1]);
    		}
    	Put(sum - Maxflow() / 2); 
    	return 0;
    }
    
  • 相关阅读:
    vue nextTick使用
    Vue slot插槽内容分发
    vue 项目设置实现通过本地手机访问
    vue router mode模式在webpack 打包上线问题
    html设置 hight100%问题
    更新模块通知栏显看不到当前进度,以及更新下载中可以清理通知问题,华为强制更新退出软件后台下载不显示通知问题
    ScrollView下嵌套GridView或ListView默认不在顶部的解决方法
    文件说明注释
    EditText双光标问题
    原 android重启应用(应用重新启动自身)
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13098060.html
Copyright © 2020-2023  润新知