[题解] CF786E ALT

题目链接

题目大意

给一颗 \(n\) 个节点的树,每个边上有一个守卫。有 \(m\) 个居民,每个居民有一个散步路径(两个节点的树上最短路)。一个居民高兴当且仅当他获得了一个宠物或者他散步的路径上所有的守卫都有宠物。宠物可以分配给居民或者守卫者。求最少需要几只宠物才能让所有居民高兴。输出方案。

思路

假设多一条狗对于答案的贡献为 \(-1\)

先让 \(m\) 个居民人手一只狗,则贡献为 \(-m\)

考虑最大权最大子图。则居民放弃一只狗的贡献为 \(1\),守卫得到一只狗的贡献为 \(-1\)

且居民放弃狗会限制守卫得到狗,则答案就为 \(-(m-(m-mincut))=mincut=maxflow\)

普通建图的时间复杂度为 \(O(nm)\),但是连边在树上一段连续的边中进行,参考T'ill It's Over的思路,用线段树(树剖)优化连边。

\(u\to[l,r]\) 可以优化为 \(u\to[l_1,r_1],u\to[l_2,r_2],\dots,u\to[l_k,r_k]\),使得 \([l_i,r_i]\) 的并集为 \([l,r]\),且没有交集,则通过 \([l_i,r_i]\) 到叶子节点,与原图等价。

最后要求输出方案,考虑怎么构造最小割。

设与原点通过残量网络连通的点形成点集 \(S\),不连通的设为点集 \(T\)

\(S\)\(T\) 的任意正向边就为最小割。

Code

#include <map>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
struct Edge1 { int To, Next; };
Edge1 edge1[MAXM << 1];
int head1[MAXN], tot1 = 1;
void Addedge1(int u, int v) {
	edge1[++tot1].Next = head1[u], edge1[tot1].To = v, head1[u] = tot1;
	edge1[++tot1].Next = head1[v], edge1[tot1].To = u, head1[v] = tot1;
}
struct Edge { int To, Cap, Next; };
Edge edge[MAXM << 1];
int head[MAXN], tot = 1;
void Addedge(int u, int v, int w) {
	edge[++tot].Next = head[u], edge[tot].To = v, edge[tot].Cap = w, head[u] = tot;
	edge[++tot].Next = head[v], edge[tot].To = u, edge[tot].Cap = 0, head[v] = tot;
}
int Min(int x, int y) { return x < y ? x : y; }
map<int, int> mp;
int cur[MAXN], dep[MAXN], que[MAXN], qhead, qtail;
int n, m, s, t, a[MAXN], b[MAXN];
bool vis[MAXN];
vector<int> vec1, vec2;
struct Node {
	int l, r;
	#define ls (pos << 1)
	#define rs (pos << 1 | 1)
};
Node tr[MAXN << 2];
int fa[MAXN], sz[MAXN], tp[MAXN], dep1[MAXN], son[MAXN], dfn[MAXN], real[MAXN];
int tim;
void dfs1(int u, int pre) {
	fa[u] = pre, sz[u] = 1, dep1[u] = dep1[pre] + 1;
	int maxn = 0;
	for(int i = head1[u]; i; i = edge1[i].Next) {
		int v = edge1[i].To;
		if(v == pre) continue;
		dfs1(v, u); sz[u] += sz[v];
		if(maxn < sz[v]) maxn = sz[v], son[u] = v;
	}
}
void dfs2(int u, int Top) {
	tp[u] = Top, dfn[u] = ++tim;
	if(son[u]) dfs2(son[u], Top);
	for(int i = head1[u]; i; i = edge1[i].Next) {
		int v = edge1[i].To;
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}
void Build(int pos, int l, int r) {
	tr[pos].l = l, tr[pos].r = r;
	if (l == r) return (void) (Addedge(m + pos, t, 1), real[pos] = l);
	int mid = (l + r) >> 1;
	Build(ls, l, mid);
	Build(rs, mid + 1, r);
	Addedge(m + pos, m + ls, INF);
	Addedge(m + pos, m + rs, INF);
}
void Update(int pos, int l, int r, int u) {
	if (l <= tr[pos].l && tr[pos].r <= r) return (void) Addedge(u, pos + m, INF);
	if (tr[ls].r >= l) Update(ls, l, r, u);
	if (tr[rs].l <= r) Update(rs, l, r, u);
}
void Update_Past(int u, int v, int x) {
	while (tp[u] != tp[v]) {
		if (dep1[tp[u]] < dep1[tp[v]]) swap(u, v);
		Update(1, dfn[tp[u]], dfn[u], x);
		u = fa[tp[u]];
	}
	if (u == v) return;
	if (dep1[u] < dep1[v]) swap(u, v);
	Update(1, dfn[son[v]], dfn[u], x);
}
bool bfs(bool limit) {
	for(int i = s; i <= t; i++) dep[i] = 0, cur[i] = head[i];
	cur[s] = head[s]; dep[s] = 1;
	qhead = 1; qtail = 1; que[1] = s;
	while(qhead <= qtail) {
		int u = que[qhead++];
		for(int i = head[u]; i; i = edge[i].Next) {
			int v = edge[i].To;
			if(!dep[v] && edge[i].Cap && (!limit || !(i & 1))) {
				dep[v] = dep[u] + 1; cur[v] = head[v];
				que[++qtail] = v;
				if (v == t) return 1;
			}
		}
	}
	return 0;
}
int dfs(int u, int flow) {
	if(u == t || !flow) return flow;
	int rest = flow;
	for(int i = cur[u]; i && rest; i = edge[i].Next) {
		cur[u] = i;
		int v = edge[i].To;
		if(dep[v] == dep[u] + 1 && edge[i].Cap) {
			int del = dfs(v, Min(rest, edge[i].Cap));
			rest -= del; edge[i].Cap -= del; edge[i ^ 1].Cap += del;
			if(!del) cur[v] = 0;
		}
	}
	return flow - rest;
}
int Dinic() {
	int res = 0, flow;
	while (bfs(1)) while ((flow = dfs(s, INF))) res += flow;
	while (bfs(0)) while ((flow = dfs(s, INF))) res += flow;
	return res;
}
void dfs_Init(int u) {
	for (int i = head1[u]; i; i = edge1[i].Next) {
		int v = edge1[i].To;
		if (mp[dfn[v]]) continue;
		mp[dfn[v]] = i / 2;
		dfs_Init(v);
	}
}
void dfs_check(int u) {
	vis[u] = 1;
	for (int i = head[u]; i; i = edge[i].Next)
		if (edge[i].Cap && !vis[edge[i].To]) dfs_check(edge[i].To);
}
int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1, u, v; i < n; i++) {
		scanf("%d %d", &u, &v);
		Addedge1(u, v);
	}
	t = 4 * n + m + 1;
	dfs1(1, 1); dfs2(1, 1); dfs_Init(1);
	Build(1, 1, n);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &a[i], &b[i]);
		Update_Past(a[i], b[i], i);
		Addedge(s, i, 1);
	}
	int res = Dinic();
	printf("%d\n", res);
	dfs_check(s);
	for (int i = head[s]; i; i = edge[i].Next)
		if (vis[s] && !vis[edge[i].To]) vec1.push_back(edge[i].To);
	printf("%llu ", vec1.size());
	for (int i = 0; i < (int) vec1.size(); i++) printf("%d%c", vec1[i], i == (int) vec1.size() - 1 ? '\n' : ' ');
	for (int i = head[t]; i; i = edge[i].Next)
		if (!vis[t] && vis[edge[i].To]) vec2.push_back(mp[real[edge[i].To - m]]);
	printf("%llu ", vec2.size());
	for (int i = 0; i < (int) vec2.size(); i++) printf("%d%c", vec2[i], i == (int) vec2.size() - 1 ? '\n' : ' ');
	return 0;
}
posted @ 2021-12-11 17:17  Last_Breath  阅读(31)  评论(0编辑  收藏  举报