bzoj 5329 [SDOI2018] 战略游戏
bzoj 5329 [SDOI2018] 战略游戏
Link
Solution
很容易想到虚树
然后发现是一个图。。。
现学圆方树,套上去,做完了(模板题?)
就是直接上广义圆方树先把这玩意转换成一棵树,然后对当前询问建立虚树,断掉虚树里任何一个点都合法(包括不出现的点,指那些在某个点和其虚树上父亲之间的点),统计一下即可
Code
// Copyright lzt
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef std::pair<int, int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<long long, long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i, j, k) for (register int i = (int)(j); i <= (int)(k); i++)
#define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__)
inline ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
x = 10 * x + ch - '0';
ch = getchar();
}
return x * f;
}
const int maxn = 200200;
int tc, n, m, Q, tot, tim;
int dfn[maxn], low[maxn], S[maxn], s[maxn], q[maxn];
int fa[maxn], dep[maxn], dis[maxn], sz[maxn], son[maxn], top[maxn];
struct Graph {
int head[maxn], to[maxn << 1], nxt[maxn << 1], tot;
void init() {
memset(head, 0, sizeof(head));
tot = 0;
}
void addedge(int x, int y) {
to[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
to[++tot] = x; nxt[tot] = head[y]; head[y] = tot;
}
} G1, G2;
void Tarjan(int u) {
tim++; dfn[u] = low[u] = tim; S[++S[0]] = u;
for (int i = G1.head[u]; i; i = G1.nxt[i]) {
int v = G1.to[i];
if (!dfn[v]) {
Tarjan(v); low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) { // 单独一条边连接两个点也得算作点双
G2.addedge(++tot, u); int x = 0;
do {
x = S[S[0]]; S[0]--;
G2.addedge(tot, x);
} while (x != v);
}
}
else low[u] = min(low[u], dfn[v]);
}
}
void dfs1(int u, int pa) {
fa[u] = pa; dep[u] = dep[pa] + 1; dis[u] = dis[pa] + (u <= n); sz[u] = 1;
for (int i = G2.head[u]; i; i = G2.nxt[i]) {
int v = G2.to[i];
if (v == pa) continue;
dfs1(v, u); sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp; dfn[u] = ++tim;
if (son[u]) dfs2(son[u], tp);
for (int i = G2.head[u]; i; i = G2.nxt[i]) {
int v = G2.to[i];
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
low[u] = tim;
}
int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
bool cmp(int x, int y) {
return dfn[x] < dfn[y];
}
void work() {
tc = read();
while (tc--) {
n = read(), m = read(); G1.init(); G2.init(); tim = 0; tot = n;
rep(i, 1, m) {
int x = read(), y = read();
G1.addedge(x, y);
}
memset(dfn, 0, sizeof(dfn));
memset(son, 0, sizeof(son));
rep(i, 1, n) if (!dfn[i]) Tarjan(i);
tim = 0; dfs1(1, 0); dfs2(1, 1);
Q = read();
while (Q--) {
int k = read(), len = k, tp = 0, ans = 0;
rep(i, 1, k) s[i] = read();
sort(s + 1, s + k + 1, cmp);
rep(i, 1, k - 1) s[++len] = lca(s[i], s[i + 1]);
sort(s + 1, s + len + 1, cmp);
len = unique(s + 1, s + len + 1) - (s + 1);
ans = s[1] <= n;
rep(i, 1, len) {
while (tp && low[q[tp]] < dfn[s[i]]) tp--;
if (tp) ans += dis[s[i]] - dis[q[tp]];
q[++tp] = s[i];
}
printf("%d\n", ans - k);
}
}
}
int main() {
#ifdef LZT
freopen("in", "r", stdin);
// freopen("out", "w", stdout);
#endif
work();
#ifdef LZT
Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
}
Review
第一次写圆方树
如果知道圆方树的话这题感觉很好想