[LOJ #6761. 最小鸽们]

[LOJ #6761. 最小鸽们]

题意

https://loj.ac/p/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\)

题解

首先这种查询最小割的东西,一看就是什么最小割树啥的。

https://loj.ac/d/3186

万老师这个帖子说的很牛逼,讲了一些树的定义,还有一种 \(\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;
}
posted @ 2021-08-02 21:11  siriehn_nx  阅读(64)  评论(0编辑  收藏  举报