• 【PR #1】守卫


    Public Round #1 T2
    题目来源:2021-2022 ICPC North America Championships. Problem I.

    题意:

    \(n(\leq 300)\) 个点 \(m\) 条边的无向图,每条边有边权。

    \(k (1 \leq k \leq n)\) 个守卫,每个守卫必须放在 \(S_i\) 个给出点集中的一个,原图中每个点最多放一个守卫。

    可以选择连一些边,要满足连上这些边后任意点都能到达任意一个守卫,求最小花费。

    kruskal重构树

    首先,选择连的边在最小生成森林上,否则可以利用树边替换非树边,方案不劣。

    关于连通性,可以想到 kruskal 重构树:

    • 只要非叶子节点的子树存在一个守卫,并且剩下的非子树部分存在一个守卫,那么就可以删去这个点代表的边。

    费用流

    这个问题可以利用费用流求解:

    这里的流量可以理解为子树内的守卫个数。

    1. 首先每个点连向父亲,费用 \(0\),容量 \(1\)

    2. 为了确保整个连通块都能到一个守卫,强制在根流,在根节点向汇点连费用 \(INF\), 容量 \(1\)的边。

    3. 对于每个非叶子节点,可以向汇点连费用 \(w_e\), 容量 \(1\) 的边,表示可以断掉这条边。

    4. 对于每个守卫,源点向它连费用为 \(0\), 容量为 \(1\) 的边;也可以向它能待的点连费用为 \(0\), 容量为 \(1\) 的边 。

    跑最大费用最大流,这样优先断深度浅的点代表的边,也就是说流出流量的点是一个包含根的连通块(应该是吧),这样就能保证正确性。

    能断掉的最大的边权和就是 \(\text{maxcost} - cnt \times INF\), \(cnt\) 表示连通块个数。

    #include<bits/stdc++.h>
    using namespace std;
    
    using ll = long long;
    using ull = unsigned long long; 
    const int MAXN = 300010;
    const int MAXM = 400010;
    const int INF = 0x3f3f3f3f;
    const int mod = 1000000007;
    //const int mod = 998244353;
    //int mod; 	
    const double eps = 1e-5; 
    
    template <typename T>
    void Read(T &x) {
    	x = 0; T f = 1; char a = getchar();
    	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
    	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
    	x *= f;
    }
    
    inline int add(const int &a, const int &b, const int p = mod) {
    	static int c;
    	c = a + b;
    	if (c >= p) c -= p;
    	return c; 
    } 
    inline int dec(const int &a, const int &b, const int p = mod) {
    	static int c;
    	c = a - b;
    	if (c < 0) c += p;
    	return c; 
    } 
    inline int mul(const int &a, const int &b, const int p = mod) {
    	return 1ll * a * b % p; 
    }
    inline int qpow(int a, ll b, const int p = mod) {
    	int sum(1);
    	if (b <= 0) return 1;
    	while(b) {
    		if (b & 1) sum = mul(sum, a, p);
    		a = mul(a, a, p);
    		b >>= 1; 
    	}
    	return sum; 
    }
    
    int n, m, k; 
    
    struct Edge {
    	int u, v, w; 
    } E[MAXM];
    
    int fa[MAXN]; 
    int getfa(int x) {
    	return fa[x] = fa[x] == x ? x : getfa(fa[x]); 
    }
    
    int s, t; 
    struct edge {
    	int next, len, point, cost; 
    } e[MAXM];
    int first[MAXN]; 
    int cnt = 1; 
    void add(int u, int v, int w, int c) {
    	++ cnt; 
    	e[cnt].point = v;
    	e[cnt].len = w;
    	e[cnt].cost = c;
    	e[cnt].next = first[u]; 
    	first[u] = cnt; 
    }
    void Add(int u, int v, int w, int c) {
    	add(u, v, w, c);
    	add(v, u, 0, -c); 
    }
    
    bool vis[MAXN]; 
    int dis[MAXN], cur[MAXN];
    bool dijkstra() {
    	for (int i = 1; i <= t; i ++)
    		dis[i] = -INF, 
    		vis[i] = 0; 
    	memcpy(cur, first, sizeof(first)); 
    	priority_queue<pair<int, int> > q;
    	dis[s] = 0; 
    	q.push(make_pair(dis[s], s));
    	while (!q.empty()) {
    		int u = q.top().second; q.pop();
    		if (vis[u]) continue;
    		vis[u] = 1; 
    		for (int i = first[u]; i; i = e[i].next) {
    			int v = e[i].point;
    			if (e[i].len && dis[v] < dis[u] + e[i].cost) {
    				dis[v] = dis[u] + e[i].cost; 
    				q.push(make_pair(dis[v], v)); 
    			}
    		}
    	}
    	return dis[t] != -INF; 
    }
    int maxcost; 
    int dfs(int u, int flow) {
    	if (u == t)
    		return flow;
    	int sum = 0;
    	vis[u] = 1; 
    	for (int &i = cur[u]; i; i = e[i].next) {
    		int v = e[i].point;
    		if (vis[v] || !e[i].len || dis[v] != dis[u] + e[i].cost) continue; 
    		int out = dfs(v, min(flow, e[i].len));
    		sum += out, flow -= out; 
    		e[i].len -= out, e[i ^ 1].len += out;
    		maxcost += out * e[i].cost; 
    		if (!flow) break; 
    	}
    	return sum; 
    }
    int dinic() {
    	int maxflow = 0;
    	while (dijkstra()) {
    		for (int i = 1; i <= t; i ++)
    			vis[i] = 0;
    		maxflow += dfs(s, INF); 
    	}	 
    	return maxflow; 
    }
    
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0); 
    	cin >> n >> m >> k; 
    	for (int i = 1; i <= m; i ++)
    		cin >> E[i].u >> E[i].v >> E[i].w; 
    	sort(E + 1, E + m + 1, [](Edge x, Edge y) {
    		return x.w < y.w; 
    	}); 
    	iota(fa + 1, fa + n + 1, 1); 
    	s = 2 * n + k + 1, t = 2 * n + k + 2; 
    	int cnt = n, node = n, ans = 0; 
    	for (int i = 1; i <= m; i ++) {
    		int x = getfa(E[i].u), y = getfa(E[i].v); 
    		if (x == y) continue;
    		int z = ++ node;
    		fa[x] = fa[y] = fa[z] = z;  
    		ans += E[i].w;
    		cnt --; 
    		Add(x, z, 1, 0);
    		Add(y, z, 1, 0);
    		Add(z, t, 1, E[i].w);  
    	}
    	for (int i = 1; i <= node; i ++)
    		if (fa[i] == i)
    			Add(i, t, 1, 1001);
    	for (int i = 1; i <= k; i ++) {
    		int len;
    		cin >> len; 
    		int u = 2 * n + i; 
    		Add(s, u, 1, 0); 
    		for (int j = 1; j <= len; j ++) {
    			int v; 
    			cin >> v;
    			Add(u, v, 1, 0); 
    		}
    	}
    	int maxflow = dinic(), d = maxcost - cnt * 1001; 
    	if (maxflow < k || d > ans || d < 0) return cout << -1, 0; 
    	cout << ans - d; 
    	return 0;		
    } 
    
  • 相关阅读:
    testNG 注解使用说明
    jenkins 生成HTML报表,邮件推送
    Jenkins 邮件发送
    Jenkins+SVN+Maven+testNG管理项目
    SVN 安装教程
    大数据笔记(十一)——倒排索引
    大数据笔记(十)——Shuffle与MapReduce编程案例(A)
    大数据笔记(九)——Mapreduce的高级特性(B)
    大数据笔记(八)——Mapreduce的高级特性(A)
    大数据笔记(七)——Mapreduce程序的开发
  • 原文地址:https://www.cnblogs.com/qjbqjb/p/16103859.html
Copyright © 2020-2023  润新知