3572: [Hnoi2014]世界树
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2025 Solved: 1086
[Submit][Status][Discuss]
Description
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所卧a与c之间的距离为2。
出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
Input
第一行为一个正整数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]],表示被授权为临时议事处的聚居地编号(保证互不相同)。
Output
输出包含q行,第i行为m[i]个整数,该行的第j(j=1,2…,,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。
Sample Input
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
Sample Output
3 1 4 1 1
10
1 1 3 5
4 1 3 1 1
HINT
N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000
分析:这道题真的好难啊......
多次询问,求若干个关键点的信息,显然用虚树做.把虚树建出来,对于虚树上的每一个点,两次dfs求出它被哪个点所控制.处理出这个,就可以来统计答案了.
分成3类来进行统计:1.每条虚树边所连接的两个点,如果都被i控制,那么这条边上点的数量就计入i的答案中.
2.如果虚树边连接的两个点分别被i,j控制,那么这条边中间一定有一个分界点,使得一边被i控制,另一边被j控制.可以通过倍增找到分界点.
3.有些点可能并不在虚树边上,这些点一定是这样的:,它们被祖先的第一个关键点给控制.所以维护一个数组g表示点i有多少个这样的点.g[i]减去前两种情况中计入i的答案.
两个dfs非常有意思,一个是自下而上,另一个是自上而下.分别考虑了点i被子节点控制和被祖先控制的情况.
这道题很容易写错,细节需要注意.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 300010; int n,head[maxn],to[maxn * 2],nextt[maxn * 2],tot = 1,pos[maxn]; int deep[maxn],sizee[maxn],fa[maxn][20],dfs_clock,Q,m,a[maxn],b[maxn]; int sta[maxn],top,belong[maxn],cnt,ans[maxn],c[maxn],g[maxn]; void add(int x,int y) { to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void add2(int x,int y) { if (x == 0 || x == y) return; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void dfs(int u,int faa) { fa[u][0] = faa; deep[u] = deep[faa] + 1; pos[u] = ++dfs_clock; sizee[u] = 1; for (int i = head[u]; i; i = nextt[i]) { int v = to[i]; if (v == faa) continue; dfs(v,u); sizee[u] += sizee[v]; } } bool cmp(int x,int y) { return pos[x] < pos[y]; } int lca(int x,int y) { if (deep[x] < deep[y]) swap(x,y); for (int i = 19; i >= 0; i--) if (deep[fa[x][i]] >= deep[y]) x = fa[x][i]; if (x == y) return x; for (int i = 19; i >= 0; i--) if (fa[x][i] != fa[y][i]) { x = fa[x][i]; y = fa[y][i]; } return fa[x][0]; } int dist(int x,int y) { return deep[x] + deep[y] - 2 * deep[lca(x,y)]; } void dfs1(int u,int faa) { c[++cnt] = u; g[u] = sizee[u]; for (int i = head[u];i; i = nextt[i]) { int v = to[i]; if (v == faa) continue; dfs1(v,u); if (belong[v] == 0) continue; if (belong[u] == 0) belong[u] = belong[v]; else { int d1 = dist(u,belong[v]),d2 = dist(u,belong[u]); if(d1 < d2 || (d1 == d2 && belong[v] < belong[u])) belong[u] = belong[v]; } } } void dfs2(int u,int faa) { for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (v == faa) continue; if (belong[v] == 0) belong[v] = belong[u]; else { int d1 = dist(v,belong[u]),d2 = dist(v,belong[v]); if (d1 < d2 || (d1 == d2 && belong[u] < belong[v])) belong[v] = belong[u]; } dfs2(v,u); //为什么不能先递归呢?因为可能belong[u] = 0 } } void solve2(int x,int y) { int son = y,jump = y; for (int i = 18; i >= 0; i--) if (deep[son] - (1 << i) > deep[x]) son = fa[son][i]; g[x] -= sizee[son]; if (belong[x] == belong[y]) { ans[belong[x]] += sizee[son] - sizee[y]; return; } for (int i = 18; i >= 0; i--) { int temp = fa[jump][i]; if (deep[temp] <= deep[x]) continue; int d1 = dist(temp,belong[x]),d2 = dist(temp,belong[y]); if (d1 > d2 || (d1 == d2 && belong[x] > belong[y])) jump = temp; } ans[belong[x]] += sizee[son] - sizee[jump]; ans[belong[y]] += sizee[jump] - sizee[y]; } void init() { for (int i = 1; i <= cnt; i++) g[c[i]] = belong[c[i]] = ans[c[i]] = head[c[i]] = 0; } void solve() { scanf("%d",&m); for (int i = 1; i <= m; i++) scanf("%d",&a[i]),b[i] = a[i]; for (int i = 1; i <= m; i++) belong[a[i]] = a[i]; sort(a + 1,a + 1 + m,cmp); top = 0; tot = 1; sta[++top] = 1; for (int i = 1; i <= m; i++) { int LCA = lca(sta[top],a[i]); while (1) { if (deep[sta[top - 1]] <= deep[LCA]) { add2(LCA,sta[top]); top--; if (sta[top] != LCA) sta[++top] = LCA; break; } add2(sta[top - 1],sta[top]); top--; } if (sta[top] != a[i]) sta[++top] = a[i]; } while (top > 1) { add2(sta[top - 1],sta[top]); top--; } top--; cnt = 0; dfs1(1,0); dfs2(1,0); for (int i = 1; i <= cnt; i++) for (int j = head[c[i]]; j; j = nextt[j]) { int v = to[j]; solve2(c[i],v); } for (int i = 1; i <= cnt; i++) ans[belong[c[i]]] += g[c[i]]; for (int i = 1; i <= m; i++) printf("%d ",ans[b[i]]); printf(" "); init(); } int main() { scanf("%d",&n); for (int i = 1; i < n; i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,0); for (int j = 1; j <= 19; j++) for (int i = 1; i <= n; i++) fa[i][j] = fa[fa[i][j - 1]][j - 1]; memset(head,0,sizeof(head)); tot = 1; scanf("%d",&Q); while (Q--) solve(); return 0; }