• 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;
    }
  • 相关阅读:
    Git 的版本库创建和修改
    appnium框架以及源码研究
    根据图片的URL生成PDF保存到本地(前台js)
    根据图片的URL生成PDF保存到服务器上(后台C#实现)
    麻烦的控件只读
    利用Javascript生成txt文本文件
    KendoUI AngularJS Bootstrap
    给Grid动态添加列和添加样式
    linq 分组求和的一般方法
    KendoUi学习之旅 Combobox的使用
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8502743.html
Copyright © 2020-2023  润新知