[LOJ #6761. 最小鸽们]
[LOJ #6761. 最小鸽们]
题意
给定一个 \(n\) 个节点 \(m\) 条带权边的无向图
\(Q\) 次询问,每次给出一个点集 \(V\), 询问 \(\sum_{u\in V} \sum_{v \in V, u \not = v} \text{MinCut}^2 (u, v)\), \(\text{MinCut}(u, v)\) 表示原图上 \(u, v\) 的最小割。
\(n\le 400, m \le 800, Q \le 1.5 \times 10^4\)
题解
首先这种查询最小割的东西,一看就是什么最小割树啥的。
万老师这个帖子说的很牛逼,讲了一些树的定义,还有一种 \(\text{Gomory-Hu Tree}\) 的构建方法。
我们发现,这玩意如果建出来 \(\text{Gomory-Hu Tree}\) 的 \(\text{kruskal}\) 重构树 的话,那么两个点的最小割就贡献在他们的 \(\text{LCA}\) 上,于是可以简单的通过一遍 \(\text{DFS}\) 解决问题,具体的对于每个节点计算他们子树之间的贡献即可。为了拥有更快的复杂度,我们或许需要建立虚树,但是题目中并没有保证 \(\sum V\)。
#include <bits/stdc++.h>
const int INF = 0x3f3f3f3f;
template<typename T>
inline T min(const T &a, const T &b) {
return a < b ? a : b;
}
template<typename T>
inline T max(const T &a, const T &b) {
return a > b ? a : b;
}
template<typename T>
inline T read() {
T x = 0;
char ch = getchar();
bool f = 0;
while (!isdigit(ch)) {
f = ch == '-';
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
const int N = 805;
using std::vector;
using std::queue;
using std::cin;
using std::cout;
using std::get;
using std::cerr;
int cnt, h[N];
struct Edge {
int to, lac, flow;
void insert(int x, int y, int z) {
flow = z;
lac = h[x];
h[x] = cnt++;
to = y;
}
} edge[N * 2], tedge[N * 2];
int n, m, id[N], cur[N], dep[N], f[N], val[N], lg[N];
vector<int> kruskal_tree[N], vt[N];
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
vector<std::tuple<int, int, int>> arr;
int dinic(int s, int t) {
std::function<bool()> bfs = [&] () {
memset(dep, -1, sizeof dep);
queue<int> q;
dep[s] = 0;
q.push(s);
for ( ; q.size(); q.pop()) {
int nw = q.front();
for (int i = h[nw], to; ~i; i = edge[i].lac) {
if (!~dep[to = edge[i].to] && edge[i].flow) {
q.push(to);
dep[to] = dep[nw] + 1;
}
}
}
memcpy(cur, h, sizeof cur);
return dep[t] != -1;
};
std::function<int(int, int)> dfs = [&] (int u, int sum) {
if (u == t) {
return sum;
}
int rem = sum;
for (int &i = cur[u], to; ~i; i = edge[i].lac) {
if (dep[to = edge[i].to] == dep[u] + 1 && edge[i].flow) {
int ret = dfs(to, min(rem, edge[i].flow));
edge[i].flow -= ret;
edge[i ^ 1].flow += ret;
rem -= ret;
if (!rem) {
break;
}
}
}
return sum - rem;
};
memcpy(edge, tedge, sizeof edge);
int maxflow = 0;
while (bfs()) {
maxflow += dfs(s, INF);
}
return maxflow;
}
void build_gt(int l, int r) {
if (l >= r) {
return;
}
// cerr << l << " " << r << std::endl;
int s = id[l], t = id[l + 1], w = dinic(s, t);
// cerr << w << ' ' << s << ' ' << t << '\n';
arr.emplace_back(w, s, t);
vector<int> L, R;
for (int i = l; i <= r; ++i) {
if (dep[id[i]] == -1) {
R.push_back(id[i]);
}
else {
L.push_back(id[i]);
}
}
int idx = l;
for (auto &j: L) {
id[idx++] = j;
}
for (auto &j: R) {
id[idx++] = j;
}
build_gt(l, l + L.size() - 1);
build_gt(l + L.size(), r);
}
int dfn[N], fa[10][N];
void dfs(int u) {
dfn[u] = ++dfn[0];
dep[u] = dep[fa[0][u]] + 1;
for (int i = 1; i <= 9; ++i) {
fa[i][u] = fa[i - 1][fa[i - 1][u]];
}
for (auto &j: kruskal_tree[u]) {
fa[0][j] = u;
dfs(j);
}
}
int LCA(int x, int y) {
if (dep[x] < dep[y]) {
std::swap(x, y);
}
while (dep[x] > dep[y]) {
x = fa[lg[dep[x] - dep[y]]][x];
}
if (x == y) {
return x;
}
for (int i = 9; ~i; --i) {
if (fa[i][x] ^ fa[i][y]) {
x = fa[i][x];
y = fa[i][y];
}
}
return fa[0][x];
}
long long ans;
int work(int u) {
if (u <= n) {
return 1;
}
int sz = 0;
for (auto &j: vt[u]) {
int tmp = work(j);
ans += 2LL * tmp * sz * val[u] * val[u];
sz += tmp;
}
return sz;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("loj6761.in", "r", stdin);
freopen("loj6761.out", "w", stdout);
#endif
std::ios::sync_with_stdio(0);
cin.tie(0);
memset(h, -1, sizeof h);
for (int i = 2; i < N; ++i) {
lg[i] = lg[i / 2] + 1;
}
cin >> n >> m;
for (int i = 1, x, y, w; i <= m; ++i) {
cin >> x >> y >> w;
tedge[cnt].insert(x, y, w);
tedge[cnt].insert(y, x, w);
}
for (int i = 1; i <= n; ++i) {
id[i] = i;
f[i] = i;
}
build_gt(1, n);
std::sort(arr.begin(), arr.end(), std::greater<std::tuple<int, int, int>>());
int ncnt = n;
for (auto &j: arr) {
int fu = find(get<1>(j)), fv = find(get<2>(j));
val[++ncnt] = get<0>(j);
f[ncnt] = f[fu] = f[fv] = ncnt;
kruskal_tree[ncnt].push_back(fu);
kruskal_tree[ncnt].push_back(fv);
//cerr << ncnt << " " << fu << " " << fv << ' ' << val[ncnt] << '\n';
}
memset(dep, 0, sizeof dep);
dfs(ncnt);
/*
for (int i = 1; i <= ncnt; ++i) {
cerr << dep[i] << " \n"[i == ncnt];
}
cerr << dfn[0] << '\n';
*/
int q, K;
for (cin >> q; q--; ) {
cin >> K;
static int a[N], stk[N], top;
vector<int> E;
for (int i = 1; i <= K; ++i) {
cin >> a[i];
}
std::sort(a + 1, a + 1 + K, [&] (int x, int y) { return dfn[x] < dfn[y]; });
stk[top = 1] = a[1];
E.push_back(a[1]);
/*
for (int i = 1; i <= K; ++i) {
std::cerr << a[i] << " \n"[i == K];
}
*/
for (int j = 2; j <= K; ++j) {
int l = LCA(a[j], stk[top]);
while (top > 1 && dep[l] <= dep[stk[top - 1]]) {
vt[stk[top - 1]].push_back(stk[top]);
--top;
}
if (l != stk[top]) {
vt[l].push_back(stk[top]);
E.push_back(stk[top] = l);
}
E.push_back(stk[++top] = a[j]);
}
for (int i = top; i > 1; --i) {
vt[stk[i - 1]].push_back(stk[i]);
}
ans = 0;
work(stk[1]);
cout << ans << '\n';
for (auto &j: E) {
vt[j].clear();
}
}
return 0;
}