题解-LibreOJ-6514「雅礼集训 2018 Day10」文明

题目链接

题目概括大力征集中~

首先感谢 这篇文章 让我学会了换根树链剖分的相关操作。

首先考虑查询次数为 1 的解法。将黈力所在的国家作为根节点,求出其他所有国家与根节点的中点。不难发现,求出的中点及其子树会被其他国家所占领。求出所有中点后,从根节点开始 dfs 整棵树,注意不能访问被标记出的中点。被访问到的节点总数即为所求。

考虑优化。用 “ dfs 序 + 线段树” 维护一个支持子树修改、子树查询的树形数据结构。每求出一个中点,就将中点及其子树的权值改为 1 。所有操作结束后,节点总数 $N$ 减去所有节点的权值总和即为所求。

对于查询次数不为 1 的数据,可以在上述数据结构中加上“换根”操作。具体实现可以参考 这篇文章 及我的代码。

略有点毒瘤。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <bits/stdc++.h>
using namespace std;
#define lson(u) node[u].l
#define rson(u) node[u].r
#define sum(u) node[u].sum
#define tag(u) node[u].tag

const int maxn = 500005;

struct Node {
int sum, l, r, tag;
}node[maxn * 3];
int R, cnt;

void build(int& u, int l, int r) {
u = ++cnt;
tag(u) = -1;
if (l == r) return;
int mid = (l + r) >> 1;
build(lson(u), l, mid);
build(rson(u), mid + 1, r);
}

inline void pushdown(int u, int l, int r) {
int mid = (l + r) >> 1;
int llen = mid - l + 1, rlen = r - mid;
if (tag(u) != -1) {
sum(lson(u)) = llen * tag(u);
sum(rson(u)) = rlen * tag(u);
tag(lson(u)) = tag(u);
tag(rson(u)) = tag(u);
tag(u) = -1;
}
}

void modify(int u, int l, int r, int ql, int qr, int val) {
if (ql <= l && r <= qr) {
sum(u) = (r - l + 1) * val;
tag(u) = val;
return;
}
pushdown(u, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) modify(lson(u), l, mid, ql, qr, val);
if (mid < qr) modify(rson(u), mid + 1, r, ql, qr, val);
sum(u) = sum(lson(u)) + sum(rson(u));
}

int query(int u, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr)
return sum(u);
pushdown(u, l, r);
int mid = (l + r) >> 1, ret = 0;
if (ql <= mid) ret += query(lson(u), l, mid, ql, qr);
if (mid < qr) ret += query(rson(u), mid + 1, r, ql, qr);
return ret;
}

int N, Q, K, root;
vector<int> G[maxn];
int f[maxn][20], dep[maxn];
int dfn[maxn], dfn_idx, size[maxn];
int p[maxn];

void dfs(int u, int fa) {
size[u] = 1;
dfn[u] = ++dfn_idx;
dep[u] = dep[fa] + 1;
f[u][0] = fa;
for (int i = 1; i <= 19; ++i)
f[u][i] = f[f[u][i - 1]][i - 1];
for (vector<int>::iterator it = G[u].begin(); it != G[u].end(); it++) {
int v = *it;
if (v == fa) continue;
dfs(v, u);
size[u] += size[v];
}
}

inline int Lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = 19; ~i; --i)
if (dep[f[u][i]] >= dep[v]) u = f[u][i];
if (u == v) return u;
for (int i = 19; ~i; --i)
if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
return f[u][0];
}

inline int Jump(int u, int d) {
for (int i = 19; ~i; --i)
if (d - (1 << i) >= 0) u = f[u][i], d -= (1 << i);
return u;
}

int main() {
scanf("%d%d", &N, &Q);
for (register int i = 1; i < N; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
build(R, 1, N);
while (Q--) {
scanf("%d", &K);
for (register int i = 1; i <= K; ++i)
scanf("%d", &p[i]);
root = p[1];
for (register int i = 2; i <= K; ++i) {
int t = p[i];
int lca = Lca(root, t);
int dis = dep[t] + dep[root] - 2 * dep[lca];
if (dis & 1) dis = (dis >> 1);
else dis = (dis >> 1) - 1;
int dis_root = dep[root] - dep[lca];
int dis_t = dep[t] - dep[lca];
int u = 0;
// Get Middle Point
if (dis <= dis_t) {
u = Jump(t, dis);
} else u = Jump(root, dis_root + dis_t - dis);
if (Lca(root, u) != u) // Situation II
modify(R, 1, N, dfn[u], dfn[u] + size[u] - 1, 1);
else { // Situation III
int v = Jump(root, dep[root] - dep[u] - 1); // * Find the nearest node *
if (dfn[v] - 1 >= 1) modify(R, 1, N, 1, dfn[v] - 1, 1);
if (dfn[v] + size[v] <= N) modify(R, 1, N, dfn[v] + size[v], N, 1);
}
}
printf("%d\n", N - query(R, 1, N, 1, N));
modify(R, 1, N, 1, N, 0);
}
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×