洛谷 P3233 [HNOI2014]世界树

世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。

世界树的形态可以用一个数学模型来描述:世界树中有 (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) 年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。

考虑建出来虚树,然后在虚树上dp,设(f_i)表示控制(i)的临时议事处的距离,(g_i)表示控制(i)的临时议事处的最小编号,则有方程

[f_u=Min_{vin son(u)}f_v+dep_v-dep_u ]

如果可以转移那么(g_u=g_v),否则如果相同就有(g_u=min(g_u,g_v))

然后计算贡献,考虑虚树上的一条边,如果两个顶点的(g)相同,那么可以直接计算贡献,如果不同,说明这两个点在原树中一定可以找到一个分界点使得该路径上下分别一种(g),这个可以在原树中倍增求得。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 3e5;
const int lg = 19;
const int inf = 1e9;
using namespace std;
struct Edge
{
    int head[N + 5],nxt[N * 2 + 5],edge[N * 2 + 5],edge_cnt;
    void add_edge(int u,int v)
    {
        edge[++edge_cnt] = v;
        nxt[edge_cnt] = head[u];
        head[u] = edge_cnt;
    }
}e1,e2;
int n,q,dep[N + 5],size[N + 5],fa[N + 5][20],dfn[N + 5],dfn_cnt,h[N + 5],id[N + 5],mp[N + 5],stk[N + 5],top,f[N + 5],g[N + 5],ans[N + 5];
void dfs1(int u,int f)
{
    fa[u][0] = f;
    dep[u] = dep[f] + 1;
    size[u] = 1;
    dfn[u] = ++dfn_cnt;
    for (int i = 1;i <= 19;i++)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = e1.head[u];i;i = e1.nxt[i])
    {
        int v = e1.edge[i];
        if (v == f)
            continue;
        dfs1(v,u);
        size[u] += size[v];
    }
}
int get_lca(int x,int y)
{
    if (dep[x] < dep[y])
        swap(x,y);
    for (int i = lg;i >= 0;i--)
        if (dep[fa[x][i]] >= dep[y])
            x = fa[x][i];
    if (x == y)
        return x;
    for (int i = lg;i >= 0;i--)
        if (fa[x][i] != fa[y][i])
            x = fa[x][i],y = fa[y][i];
    return fa[x][0];
}
bool cmp(int a,int b)
{
    return dfn[a] < dfn[b];
}
void insert(int x)
{
    if (!top)
    {
        stk[++top] = x;
        return;
    }
    int lca = get_lca(stk[top],x);
    while (top > 1 && dep[stk[top - 1]] > dep[lca])
    {
        e2.add_edge(stk[top],stk[top - 1]);
        e2.add_edge(stk[top - 1],stk[top]);
        top--;
    }
    if (lca != stk[top])
    {
        e2.add_edge(stk[top],lca);
        e2.add_edge(lca,stk[top]);
        top--;
    }
    if (!top || stk[top] != lca)
        stk[++top] = lca;
    stk[++top] = x;
}
void dfs2(int u,int fa)
{
    if (mp[u])
        f[u] = 0,g[u] = u;
    else
        f[u] = inf;
    //cout<<u<<"QAQ"<<endl;
    for (int i = e2.head[u];i;i = e2.nxt[i])
    {
        int v = e2.edge[i],dis = dep[v] - dep[u];
        if (v == fa)
            continue;
        dfs2(v,u);
        if (f[v] + dis < f[u])
            f[u] = f[v] + dis,g[u] = g[v];
        else
            if (f[v] + dis == f[u])
                g[u] = min(g[u],g[v]);
    }
}
void calc(int u,int v)
{
    int x = v,dp1,dp2;
    for (int i = lg;i >= 0;i--)
    {
        dp1 = f[v] + dep[v] - dep[fa[x][i]];
        dp2 = f[u] + dep[fa[x][i]] - dep[u];
        if (dep[fa[x][i]] > dep[u] && (dp1 < dp2 || dp1 == dp2 && g[v] < g[u]))
            x = fa[x][i];
    }
    ans[g[v]] += size[x] - size[v];
    ans[g[u]] -= size[x];
}
void dfs3(int u,int fa)
{
    for (int i = e2.head[u];i;i = e2.nxt[i])
    {
        int v = e2.edge[i],dis = dep[v] - dep[u];
        if (v == fa)
            continue;
        if (f[u] + dis < f[v])
            f[v] = f[u] + dis,g[v] = g[u];
        else
            if (f[u] + dis == f[v])
                g[v] = min(g[v],g[u]);
        calc(u,v);
        dfs3(v,u);
    }
    ans[g[u]] += size[u];
    mp[u] = e2.head[u] = 0;
}
int main()
{
    scanf("%d",&n);
    int u,v;
    for (int i = 1;i < n;i++)
    {
        scanf("%d%d",&u,&v);
        e1.add_edge(u,v);
        e1.add_edge(v,u);
    }
    dfs1(1,0);
    scanf("%d",&q);
    int m;
    while (q--)
    {
        scanf("%d",&m);
        top = e2.edge_cnt = 0;
        id[m + 1] = 0;
        for (int i = 1;i <= m;i++)
            scanf("%d",&h[i]),mp[h[i]] = 1,ans[h[i]] = 0;
        for (int i = 1;i <= m;i++)
            id[i] = h[i];
        if (!mp[1])
            h[++m] = 1;
        sort(h + 1,h + m + 1,cmp);
        for (int i = 1;i <= m;i++)
            insert(h[i]);
        while (top > 1)
        {
            e2.add_edge(stk[top],stk[top - 1]);
            e2.add_edge(stk[top - 1],stk[top]);
            top--;
        }
        dfs2(1,0);
        dfs3(1,0);
        if (!id[m])
            m--;
        for (int i = 1;i <= m;i++)
            printf("%d ",ans[id[i]]);
        putchar(10);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/sdlang/p/13336239.html