题目大意:
割掉权值和最小的边,使得根节点s与度为1的点都没有连边。
Solution:
关于割边问题,我们很容易想到最小割,又有最大流=最小割,可以直接上dinic了。
建一个超级源点,连接所有度为1的边(根节点除外),容量为INF,s为汇点,然后跑dinic求出最大流即为答案。
网络流度理论时间复杂度一直都很玄学,但实际上跑起来的速度是很快的。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll N = 2e6 + 5;
const ll INF = 0x3fffffffffffff;
ll to[N], f[N], ne[N], h[N], idx;
ll n, m, s, de[N], dp[N];
void add(ll a, ll b, ll c) {
to[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
to[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}
ll dfs(ll now, ll flow, ll t) {
if (now == t) return flow;
ll sum = 0;
for (ll i = h[now]; i != -1 && flow; i = ne[i]) {
if (f[i] && dp[to[i]] == dp[now] + 1) {
ll x = dfs(to[i], min(flow, f[i]), t);
f[i] -= x, f[i ^ 1] += x, flow -= x, sum += x;
}
}
if (!sum) dp[now] = -2;
return sum;
}
bool bfs(ll s, ll t) {
queue<int> q;
memset(dp, 0, sizeof(dp));
dp[s] = 1;
q.push(s);
while (!q.empty()) {
ll x = q.front();
q.pop();
for (ll i = h[x]; i != -1; i = ne[i]) {
if (f[i] && !dp[to[i]]) {
q.push(to[i]);
dp[to[i]] = dp[x] + 1;
if (to[i] == t) return true;
}
}
}
return false;
}
ll dinic(ll s, ll t) {
ll res = 0;
while (bfs(s, t)) res += dfs(s, INF, t);
return res;
}
int main() {
memset(h, -1, sizeof(h));
scanf("%lld%lld%lld", &n, &m, &s);
for (ll i = 1; i <= m; i++) {
ll x, y, z;
scanf("%lld%lld%lld", &x, &y, &z);
add(x, y, z);
add(y, x, z);
de[x]++;
de[y]++;
}
for (ll i = 1; i <= n; i++) {
if (de[i] == 1 && i != s) add(0, i, INF);
}
printf("%lld\n",dinic(0, s));
return 0;
}

京公网安备 11010502036488号