【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 重构树:
- 只要非叶子节点的子树存在一个守卫,并且剩下的非子树部分存在一个守卫,那么就可以删去这个点代表的边。
费用流
这个问题可以利用费用流求解:
这里的流量可以理解为子树内的守卫个数。
-
首先每个点连向父亲,费用 \(0\),容量 \(1\)。
-
为了确保整个连通块都能到一个守卫,强制在根流,在根节点向汇点连费用 \(INF\), 容量 \(1\)的边。
-
对于每个非叶子节点,可以向汇点连费用 \(w_e\), 容量 \(1\) 的边,表示可以断掉这条边。
-
对于每个守卫,源点向它连费用为 \(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;
}
标签:PR,cnt,const,int,len,守卫,return,dis 来源: https://www.cnblogs.com/qjbqjb/p/16103859.html