• Luogu P4249 【[WC2007]剪刀石头布】


    Description

    传送门


    Solution

    首先发现直接求这种三元组不打好求,那么考虑球不满足这种关系的三元组的数量。

    注意到一个三元组,如果不满足这种关系,肯定分别赢了(0)(1)(2)场。

    那么我们如果知道了每个人赢的场数(y_i),不具有这种关系的三元组数量就是(sum frac{y_i imes (y_i - 1}{2})

    因为要使满足这种关系的三元组数量最多,所以我们建立的合法方案要使(sum frac{y_i imes (y_i - 1}{2})最小。考虑用网络流来求解。

    首先要满足是一种合法方案,可以这样建模,首先每个人向(T)连一条容量为(n)的有向边,然后对于一个还没有发生的比赛,新建一个节点(tmp),从(tmp)分别向(i)(j)连一条容量为(1)的有向边,从(S)(tmp)链接一条容量为(1)的有向边。这样建模跑出来的最大流就是一种合法方案。

    现在考虑如何在跑最大流的时候使(sum frac{y_i imes (y_i - 1)}{2})最小,将每个人向(T)连的有向边拆成容量为(1)的若干条有向边,费用分别为(0)(1)(2 dots)这样连边是因为如果一个人的胜利场数增加一,(frac{y_i imes (y_i - 1}{2})的变化会呈现一种等差数列的形式,而这样将边拆开,每次只会增广费用最小的一条路,所以能保证正确性。

    需要注意的是,如果一个人本来有一些胜利场数,要在原先的胜利场次基础上进行拆边和建模。


    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    
    #define int long long
    
    const int INF = 999999999;
    const int N = 1000050;
    const int M = 2000050;
    
    int n, s, t, head[N], cur[N], d[N], vis[N], a[500][500], num = 1, cnt, y[N], ans, maxflow, mincost, neww[N][3], diao;
    
    struct Node
    {
    	int next, to, flow, dis;
    } edge[M * 2];
    
    void Addedge(int u, int v, int w, int c)
    {
    	edge[++num] = (Node){ head[u], v, w, c};
    	head[u] = num;
    }
    
    void Add(int u, int v, int w, int c)
    {
    	Addedge(u, v, w, c);
    	Addedge(v, u, 0, -c);
    	return; 
    }
    
    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; 
    }
    
    void Work(int x)
    {
    	for (int i = y[x] + 1; i <= n; i++)
    	{
    		Add(x, t, 1, i - 1);
    	}
    	return;
    }
    
    int Bfs()
    {
    	queue<int> q;
    	for (int i = 0; i <= cnt; i++) d[i] = INF, vis[i] = 0;
    	d[s] = 0; vis[s] = 1; q.push(s);
    	while (!q.empty())
    	{
    		int u = q.front(); q.pop(); vis[u] = 0;
    		for (int i = head[u]; i; i = edge[i].next)
    			if (edge[i].flow > 0 && d[edge[i].to] > d[u] + edge[i].dis)
    			{
    				d[edge[i].to] = d[u] + edge[i].dis;
    				if (!vis[edge[i].to])
    				{
    					vis[edge[i].to] = 1;
    					q.push(edge[i].to);
    				}
    			}	
    	} 
    	return d[t] != INF;
    }
    
    int Dinic(int x, int flow)
    {
    	if (x == t || !flow) return flow;
    	int rest = flow, k;
    	vis[x] = 1;
    	for (int i = cur[x]; i && rest; i = edge[i].next)
    	{
    		int v = edge[i].to;
    		cur[x] = i;
    		if (!vis[edge[i].to] && edge[i].flow > 0 && d[edge[i].to] == d[x] + edge[i].dis)
    		{
    			int v = edge[i].to;
    			int k = Dinic(v, min(rest, edge[i].flow));
    		//	if (!k) dis[edge[i].to] = INF;
    			rest -= k;
    			edge[i].flow -= k;
    			edge[i ^ 1].flow += k;
    			mincost += k * edge[i].dis;
    		}
    	}
    	vis[x] = 0;
    	return flow - rest;
    }
    
    void Solve()
    {
    	while(Bfs())
    	{
    		for (int i = 0; i <= cnt; i++) cur[i] = head[i]; 
    		maxflow += Dinic(s, INF);
    	}
    	return; 
    }
    
    signed main()
    {
    	Read(n);
    	cnt = n + 1;
    	for (int i = 1; i <= n; i++) 
    		for (int j = 1; j <= n; j++)
    		{
    			Read(a[i][j]);
    			if (a[i][j] == 1) y[i]++;
    		}
    	s = 0; t = n + 1;
    	for (int i = 1; i <= n; i++) ans += y[i] * (y[i] - 1) / 2, Work(i);
    	for (int i = 1; i <= n; i++)
    		for (int j = i + 1; j <= n; j++)
    			if (a[i][j] == 2)
    			{
    				int tmp = ++cnt;
    				neww[++diao][1] = i;
    				neww[diao][2] = j; 
    				Add(s, tmp, 1, 0);
    				Add(tmp, j, 1, 0); 
    				Add(tmp, i, 1, 0);
    				neww[diao][0] = num;
    			} 
    	Solve();
    	ans += mincost;
    	Put(n * (n - 1) * (n - 2) / 6 - ans); putchar('
    ');
    	for (int i = 1; i <= diao; i++) a[neww[i][1]][neww[i][2]] = edge[neww[i][0]].flow > 0 ? 1 : 0, a[neww[i][2]][neww[i][1]] = a[neww[i][1]][neww[i][2]] ^ 1;
     	for (int i = 1; i <= n; i++) 
    	{
    		for (int j = 1; j <= n; j++)
    			Put(a[i][j]), putchar(' ');
    		putchar('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    Service
    adb server is out of date
    Notification.Builder
    eclipse连接小米2调试程序的问题
    Date类
    this指向冒泡排序二分查找
    Dom事件键盘事件
    Dom事件键盘事件
    12.4Dom事件键盘事件
    事件对象
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13098033.html
Copyright © 2020-2023  润新知