CF613D Kingdom and its Cities (虚树优化 dp)
虚树优化 dp
考虑无解的情况,若有两个重要城市相邻,那么无解。
对于有解的情况,朴素的如何求解最少占领的城市数?考虑从叶子节点开始向上贪心,假如当前 \(u\) 节点为关键点,那么对于它的子树 \(v\),若它的关键点能到 \(v\),就要和他断开。如果 \(u\) 节点不是关键点,那么如果出现两个以上的子树有关键点,他自己就要断开;否则不考虑断开。每次询问跑一次,那么总复杂度就是 \(O(nq)\)。考虑优化。
我们发现两两关键点之间的边是不重要的,除此之外,两两关键点的 \(lca\) 是重要的,它可以考虑到一个点断开多个点的情况。于是对每个询问建个虚树,就做完了。
复杂度 \(O(n\log n)\)。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back
typedef long long i64;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int n, m, q, tot, ans;
std::vector<int> V[N];
int anc[N][20], dfn[N], dep[N], a[N];
void dfs(int u, int fa) {
anc[u][0] = fa;
dfn[u] = ++tot;
dep[u] = dep[fa] + 1;
for(int j = 1; j <= 19; j++) {
anc[u][j] = anc[anc[u][j - 1]][j - 1];
}
for(auto v : V[u]) {
if(v == fa) continue;
dfs(v, u);
}
}
int lca(int u, int v) {
if(dep[u] < dep[v]) std::swap(u, v);
for(int i = 19; i >= 0; i--) if(dep[anc[u][i]] >= dep[v]) u = anc[u][i];
if(u == v) return u;
for(int i = 19; i >= 0; i--) if(anc[u][i] != anc[v][i]) u = anc[u][i], v = anc[v][i];
return anc[u][0];
}
int cnt;
int h[N];
struct node {
int to, nxt;
} e[N << 1];
void add(int u, int v) {
e[++cnt].to = v, e[cnt].nxt = h[u];
h[u] = cnt;
}
bool cmp(int a, int b) {
return dfn[a] < dfn[b];
}
int st[N], top;
int f[N], vis[N];
void build() {
std::sort(a + 1, a + m + 1, cmp);
for(int i = 1; i <= m; i++) {
int rt = lca(a[i], st[top]);
while(top && dep[st[top - 1]] >= dep[rt]) {
add(st[top - 1], st[top]), top--;
}
if(st[top] != rt) {
add(rt, st[top]), st[top] = rt;
}
st[++top] = a[i];
}
while(top > 1) {
add(st[top - 1], st[top]);
top--;
}
}
void dp(int u, int fa) {
int sum = 0;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dp(v, u);
if(vis[v]) sum++;
}
if(vis[u]) {
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
if(vis[v]) ans++;
}
} else {
if(sum > 1) ans++;
else if(sum == 1) vis[u] = 1;
}
}
void clear(int u, int fa) {
vis[u] = f[u] = 0;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
clear(v, u);
}
h[u] = 0;
}
void Solve() {
std::cin >> n;
for(int i = 1; i < n; i++) {
int u, v;
std::cin >> u >> v;
V[u].pb(v), V[v].pb(u);
}
dfs(1, 0);
std::cin >> q;
while(q--) {
std::cin >> m;
for(int i = 1; i <= m; i++) {
std::cin >> a[i];
vis[a[i]] = 1;
}
bool flg = 0;
for(int i = 1; i <= m; i++) {
if(vis[anc[a[i]][0]]) flg = 1;
}
if(flg) {
for(int i = 1; i <= m; i++) vis[a[i]] = 0;
std::cout << "-1\n";
continue;
}
build();
dp(st[1], 0);
std::cout << ans << "\n";
f[st[1]] = 0;
ans = cnt = top = 0;
clear(st[1], 0);
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
Solve();
return 0;
}