CF613D Kingdom and its Cities
虚树 + 贪心。
首先考虑只有一个询问的情况,本质上是一个树上从下到上的贪心,方法如下:
1、先判$-1$,只要在输入的点中有两个相邻的情况,那么一定无解。
2、从上到下$dfs$,先对子树内所有的答案求个和,如果搜到一个点是关键点,那么这个点一定不能选,但是它子树中在所有关键点的路径上的点必须要选一个,如果不是一个关键点,要看看它的子树中是否有两个及以上的关键点,如果是,那么一定是切断这个点更优,否则就让它的父亲来计算贡献。
然后就是一个虚树的板子。
时间复杂度应该是$O(nlogn)$。
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1e5 + 5; const int Lg = 20; const int inf = 1 << 30; int n, qn, tot = 0, head[N], fa[N][Lg], dep[N]; int a[N * 2], dfsc = 0, in[N], out[N], top = 0, sta[N * 2]; int sum[N], f[N], g[N], siz[N]; bool vis[N], flag[N]; struct Edge { int to, nxt; } e[N << 1]; inline void add(int from, int to) { e[++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } bool cmp(int x, int y) { int dfx = x > 0 ? in[x] : out[-x]; int dfy = y > 0 ? in[y] : out[-y]; return dfx < dfy; } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void swap(int &x, int &y) { int t = x; x = y; y = t; } void dfs(int x, int fat, int depth) { dep[x] = depth, fa[x][0] = fat; in[x] = ++dfsc; for(int i = 1; i <= 18; i++) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs(y, x, depth + 1); } out[x] = ++dfsc; } inline int getLca(int x, int y) { if(dep[x] < dep[y]) swap(x, y); for(int i = 18; i >= 0; i--) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i]; if(x == y) return x; for(int i = 18; i >= 0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } inline bool chk(int K) { for(int i = 1; i <= K; i++) if(flag[fa[a[i]][0]]) return 0; return 1; } inline void solve() { int K, cnt; read(K); for(int i = 1; i <= K; i++) { read(a[i]); if(!vis[a[i]]) flag[a[i]] = vis[a[i]] = 1; } cnt = K; if(!chk(K)) { puts("-1"); } else { sort(a + 1, a + 1 + K, cmp); for(int i = 1; i < cnt; i++) { int now = getLca(a[i], a[i + 1]); if(!vis[now]) vis[now] = 1, a[++cnt] = now; } for(int cur = cnt, i = 1; i <= cur; i++) a[++cnt] = -a[i]; if(!vis[1]) a[++cnt] = 1, a[++cnt] = -1; sort(a + 1, a + 1 + cnt, cmp); top = 0; for(int i = 1; i <= cnt; i++) { if(a[i] > 0) sta[++top] = a[i]; else { int x = sta[top--], y = sta[top]; if(flag[x]) f[x] += siz[x], g[x] = 1; else f[x] += (siz[x] > 1), g[x] = (siz[x] == 1); if(y) f[y] += f[x], siz[y] += g[x]; if(x == 1) printf("%d\n", f[x]); } } } for(int i = 1; i <= cnt; i++) if(a[i] > 0) f[a[i]] = g[a[i]] = sum[a[i]] = siz[a[i]] = 0, flag[a[i]] = vis[a[i]] = 0; } int main() { read(n); for(int x, y, i = 1; i < n; i++) { read(x), read(y); add(x, y), add(y, x); } dfs(1, 0, 1); for(read(qn); qn--; ) solve(); return 0; }