(color{#0066ff}{ 题目描述 })
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有 (n) 个种族,种族的编号分别从 (1) 到 (n),分别生活在编号为 (1) 到 (n) 的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为 (1)。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地 (a) 和 (b) 之间有道路,(b) 和(c) 之间有道路,因为每条道路长度为 (1) 而且又不可能出现环,所以 (a) 与(c) 之间的距离为 (2)。
出于对公平的考虑,第 (i) 年,世界树的国王需要授权 (m_i) 个种族的聚居地为临时议事处。对于某个种族(x)x((x)x 为种族的编号),如果距离该种族最近的临时议事处为 (y)((y) 为议事处所在聚居地的编号),则种族 (x) 将接受 (y) 议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则 (y) 为其中编号最小的临时议事处)。
现在国王想知道,在 (q) 年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
(color{#0066ff}{输入格式})
第一行为一个正整数n,表示世界树中种族的个数。接下来n-l行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双向道路。接下来一行为一个正整数q,表示国王询问的年数。接下来q块,每块两行:第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。第i块的第二行为m[i]个正整数h[l]、h[2]、...、h[m[i]],表示被授权为临时议事处的聚居地编号(保证互不相同)。
(color{#0066ff}{输出格式})
输出包含q行,第i行为m[i]个整数,该行的第j(j=1,2...,,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。
(color{#0066ff}{输入样例})
10
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
5
2
6 1
5
2 7 3 6 9
1
8
4
8 7 10 3
5
2 9 3 5 8
(color{#0066ff}{输出样例})
1 9
3 1 4 1 1
10
1 1 3 5
4 1 3 1 1
(color{#0066ff}{数据范围与提示})
N<=300000, q<=300000,m[1]+m[2]+...+m[q]<=300000
(color{#0066ff}{ 题解 })
由题可知关键点是比较少的
可以用虚树来做
虚树就是原树中,只保留一些关键点以及互相通达的路径(就是所有LCA也要留下)其余点忽略构成的树
用栈来维护一天深度递增的链,每次来一个点,先找它的LCA
为了防止栈顶与栈顶-1两个点跨过LCA,先把没用的弹掉,特盘一下LCA连边, 然后改变链的方向
构建完虚树后,开始收集ans
dfs1,处理出dep,siz,倍增LCA的一些东西
dfs2,求出虚树上每个点到其子树内的关键的点的最小距离及其编号(用pair),记为d[i]
dfs3,维护当前子树之外的最小距离和编号,使d[i]成为当前点到关键点的最小距离
dfs4,可以发现,对于虚树上的某一点,如果他的孩子的子树中没有关键点,那么这个点就会收集到子树内所有点的贡献(从虚树上的儿子倍增,找到原树上的儿子)
用单步容斥,先加上当前子树的siz,再减去所有有关键点的子树siz
dfs5, 统计子树内有关键点中的贡献,那么两个关键点必为一浅一深,找到中间深度,然后上半部分的所有点(除了当前子树,用siz作差)都是x所属的,剩下的是子树所属的
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
struct node{
int to;
node *nxt;
node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
void *operator new (size_t) {
static node *S = NULL, *T = NULL;
return (S == T) && (T = (S = new node[1024]) + 1024), S++;
}
};
const int maxn = 3e5 + 100;
const int inf = 0x7f7f7f7f;
node *h[maxn], *head[maxn];
int dep[maxn], a[maxn], b[maxn], dfn[maxn], p[maxn];
int st[maxn], top;
int n, cnt;
using std::pair;
using std::make_pair;
pair<int, int> d[maxn];
int f[maxn][27], siz[maxn], ans[maxn], up[maxn];
bool vis[maxn];
void add(int from, int to, node **hh) {
hh[from] = new node(to, hh[from]);
}
void dfs1(int x, int fa) {
dep[x] = dep[fa] + 1;
f[x][0] = fa;
siz[x] = 1;
dfn[x] = ++cnt;
for(node *i = h[x]; i; i = i->nxt)
if(i->to != fa) dfs1(i->to, x), siz[x] += siz[i->to];
}
void dfs2(int x) {
if(vis[x]) d[x] = make_pair(0, x);
else d[x] = make_pair(inf, 0);
for(node *i = head[x]; i; i = i->nxt)
dfs2(i->to), d[x] = std::min(d[x], make_pair(d[i->to].first + dep[i->to] - dep[x], d[i->to].second));
}
void dfs3(int x, int dis, int pos) {
if(d[x] > make_pair(dis, pos)) d[x] = make_pair(dis, pos);
else dis = d[x].first, pos = d[x].second;
for(node *i = head[x]; i; i = i->nxt)
dfs3(i->to, dis + dep[i->to] - dep[x], pos);
}
void dfs4(int x) {
p[x] = d[x].second;
ans[p[x]] += siz[x];
for(node *i = head[x]; i; i = i->nxt) {
int o = i->to;
for(int j = 25; j >= 0; j--) if(dep[f[o][j]] > dep[x]) o = f[o][j];
ans[p[x]] -= siz[up[i->to] = o];
dfs4(i->to);
}
}
void dfs5(int x) {
for(node *i = head[x]; i; i = i->nxt) {
int u = up[i->to];
if(p[x] == p[i->to]) ans[p[x]] += siz[u] - siz[i->to];
else {
int mid = dep[p[i->to]] + dep[x] - d[x].first;
//如果是一条链,很容易发现是正确的
//不是一条直链,因为x最近的不是子树最近的,所以mid一定在x到子树的链上
mid = mid & 1? (mid + 1) >> 1 : (p[x] < p[i->to]? (mid >> 1) + 1 : (mid >> 1));
int o = i->to;
for(int j = 25; j >= 0; j--) if(dep[f[o][j]] >= mid) o = f[o][j];
ans[p[x]] += siz[u] - siz[o];
ans[p[i->to]] += siz[o] - siz[i->to];
}
dfs5(i->to);
}
}
void dfs6(int x) {
up[x] = p[x] = 0;
for(node *i = head[x]; i; i = i->nxt)
dfs6(i->to);
head[x] = NULL;
}
int LCA(int x, int y) {
if(dep[x] < dep[y]) std::swap(x, y);
for(int i = 25; i >= 0; i--) if(dep[f[x][i]] >= dep[y]) x = f[x][i];
if(x == y) return x;
for(int i = 25; i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
int main() {
n = in();
int x, y;
for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y, h), add(y, x, h);
dfs1(1, 0);
for(int j = 1; j <= 25; j++)
for(int i = 1; i <= n; i++)
f[i][j] = f[f[i][j - 1]][j - 1];
for(int T = in(); T --> 0;) {
int num = in();
for(int i = 1; i <= num; i++) b[i] = a[i] = in(), vis[a[i]] = true;
std::sort(a + 1, a + num + 1, [](const int &x, const int &y) { return dfn[x] < dfn[y]; });
st[top = 1] = a[1];
for(int i = 2; i <= num; i++) {
int now = a[i], lca = LCA(st[top], now);
while(top > 1 && dep[st[top - 1]] >= dep[lca]) add(st[top - 1], st[top], head), top--;
if(st[top] != lca) add(lca, st[top], head), st[top] = lca;
st[++top] = now;
}
while(top > 1) add(st[top - 1], st[top], head), top--;
int root = st[1];
dfs2(root);
dfs3(root, d[root].first, d[root].second);
dfs4(root), dfs5(root);
ans[p[root]] += siz[1] - siz[root];
for(int i = 1; i <= num; i++) printf("%d%c", ans[b[i]], i == num? '
' : ' ');
dfs6(root);
for(int i = 1; i <= num; i++) vis[a[i]] = 0, ans[a[i]] = 0;
}
return 0;
}