BZOJ5329 [SDOI2018] 战略游戏
2018 年 05 月 17 日发布.
Description
一个 $n$ 个点 $m$ 条边的无向连通图,$q$ 次询问。每次询问给出一个集合 $S$ ,问有多少结点 $x$ 满足:
- $x\not\in S$;
- 删除结点 $x$ 及与其相连的所有边之后, $S$ 中的点不连通。
$n,q\leqslant10^5;m,\sum|S|\leqslant2\times10^5$ 。
多组数据,数组组数 $T\leqslant10$ ;时限 $10s$ 。
Solution
首先,删除某结点后 $x$ 和 $y$ 不连通,这等价于这个结点在所有 $x$ 到 $y$ 的简单路径上。
圆方树基本性质:原图上 $x$ 到 $y$ 的所有简单路径交即为圆方树上 $x$ 到 $y$ 的所有圆点。
那么我们建出圆方树;可以发现答案就是点集 $S$ 建出的虚树中除 $S$ 里所有点之外的圆点个数。
所以暴力建一遍虚树就可以了。时间复杂度 $O(n\log n+\sum(|S|\log|S|))$ ( RMQ$O(1)$LCA )。
考场上 yy 出一个正确的点双结果由于 RMQ 数组开小爆零了... 。
Code
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
const int L = 10000000;
inline char getChar() {
static char s[L], *end = s, *p = s;
if (p == end) {
if (feof(stdin)) return EOF;
= s + fread(p = s, 1, L, stdin);
end }
return *(p++);
}
int readInt() {
int ans = 0, c;
while (!isdigit(c = getChar()));
do ans = ans * 10 + c - '0';
while (isdigit(c = getChar()));
return ans;
}
char s[L], *p = s, *end = s + L;
inline void putChar(char c) {
if (p == end) fwrite(p = s, 1, L, stdout);
*(p++) = c;
}
void printInt(int x, char c = ' ') {
if (x >= 10) printInt(x / 10, EOF);
(x % 10 + '0');
putCharif (c != EOF) putChar(c);
}
const int N = 100050;
const int M = 400050;
int n, m, pre[N], nxt[M], to[M], cnt;
inline void addEdge(int x, int y) {
[cnt] = pre[x];
nxt[pre[x] = cnt++] = y;
to[cnt] = pre[y];
nxt[pre[y] = cnt++] = x;
to}
namespace Tree{
const int N = 200050;
int n, pre[N], nxt[N * 2], to[N * 2], cnt;
bool ty[N]; // true: circle; false: square
inline void addEdge(int x, int y) {
[cnt] = pre[x];
nxt[pre[x] = cnt++] = y;
to[cnt] = pre[y];
nxt[pre[y] = cnt++] = x;
to}
int dep2[N], pos[N], dep[N], nd[N * 2], cnt2;
void dfs(int x, int fa) {
[x] = dep2[fa] + ty[x];
dep2[pos[x] = ++cnt2] = x;
nd[x] = dep[fa] + 1;
depfor (int i = pre[x]; i >= 0; i = nxt[i])
if (to[i] != fa) {
(to[i], x);
dfs[++cnt2] = x;
nd}
}
inline int mind(int x, int y) { return dep[x] < dep[y] ? x : y; }
int minv[20][N * 2], log2[N * 2];
void InitRMQ() {
for (int i = 1; i <= cnt2; ++i) minv[0][i] = nd[i];
for (int i = 1; (1 << i) <= cnt2; ++i)
for (int j = 1; (j + (1 << i)) <= cnt2 + 1; ++j)
[i][j] = mind(minv[i - 1][j], minv[i - 1][j + (1 << (i - 1))]);
minvfor (int i = 1, j = 0; i <= cnt2; ++i)
[i] = j += (i >= (1 << (j + 1)));
log2}
inline int LCA(int x, int y) {
if ((x = pos[x]) > (y = pos[y])) std::swap(x, y);
int k = log2[y - x + 1];
return mind(minv[k][x], minv[k][y - (1 << k) + 1]);
}
inline int count(int x, int y) {
int l = LCA(x, y);
return dep2[x] + dep2[y] - 2 * dep2[l] + ty[l] - ty[x] - ty[y];
}
bool cmp(int a, int b) { return pos[a] < pos[b]; }
int A[N];
int stack[N], top;
void Solve() {
= 0;
cnt2 (1, 0);
dfs();
InitRMQint q = readInt();
while (q--) {
int s = readInt();
for (int i = 0; i < s; ++i) A[i] = readInt();
std::sort(A, A + s, cmp);
int ans = 0;
= 0;
top for (int i = 0; i < s; ++i) {
int x = A[i];
while (top > 1 && LCA(stack[top - 2], x) == LCA(stack[top - 1], x)) {
+= count(stack[top - 2], stack[top - 1]);
ans --top;
}
if (top > 0) {
int l = LCA(stack[top - 1], x);
if (l != stack[top - 1]) {
+= count(stack[top - 1], l) + ty[l];
ans [top - 1] = l;
stack}
}
[top++] = x;
stack}
while (top > 1) ans += count(stack[top - 2], stack[top - 1]), --top;
(ans, '\n');
printInt}
}
};
int dfn[N], cnt2;
int stack[N], top, bcnt;
int Tarjan(int x, int fa) {
int lowx = dfn[x] = ++cnt2;
[top++] = x;
stackfor (int i = pre[x]; i >= 0; i = nxt[i])
if (to[i] != fa) {
if (dfn[to[i]] == 0) lowx = std::min(lowx, Tarjan(to[i], x));
else lowx = std::min(lowx, dfn[to[i]]);
}
if (lowx >= dfn[fa]) {
int nn = n + (++bcnt);
::ty[nn] = false;
Tree::addEdge(x, nn);
Treeif (fa > 0) Tree::addEdge(fa, nn);
while (stack[--top] != x) Tree::addEdge(stack[top], nn);
}
return lowx;
}
int main() {
//freopen("game.in", "r", stdin);
//freopen("game.out", "w", stdout);
int T = readInt();
while (T--) {
= readInt();
n = readInt();
m (pre, -1, sizeof pre);
memset(Tree::pre, -1, sizeof Tree::pre);
memset= Tree::cnt = 0;
cnt for (int i = 0; i < m; ++i)
(readInt(), readInt());
addEdgefor (int i = 1; i <= n; ++i) Tree::ty[i] = true;
(dfn, 0, sizeof dfn);
memset= bcnt = 0;
top (1, 0);
Tarjan::n = n + bcnt;
Tree::Solve();
Tree}
(s, 1, p - s, stdout);
fwritereturn 0;
}