bzoj3572 [Hnoi2014]世界树

3572: [Hnoi2014]世界树

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 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

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

Sample Output

1 9
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;
}
原文地址:https://www.cnblogs.com/zbtrs/p/8502743.html