CH#56C 异象石(最近公共祖先)(DFS序)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/82919640

题目

Adera是Microsoft应用商店中的一款解谜游戏。
异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图。这张地图上有N个点,有N-1条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的M个时刻中,每个时刻会发生以下三种类型的事件之一:
1. 地图的某个点上出现了异象石(已经出现的不会再次出现);
2. 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
3. 向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。
请你作为玩家回答这些问题。

题解

最近公共祖先+DFS序

简化题意后,这题就是一个动态求最小连接多点路径的问题
对于这类问题,我们先跑一遍dfs,求出dfs序。对于一个新插入的点x,找到它dfs序相邻的两个出现异象石的节点,即为l和r。我们可以得到新增的路径长为( -path(l,r)+path(l,x)+path(x,r) )/2。其实相当于在dfs序上处理这些问题,因为dfs序涉及入和出,所以才有除以2。再想,先除去path(l,r),然后加上(l,x)和(x,r),很好的表示了新的最小长度。删除节点是大同小异,把这个点删掉新增的路径为( -(path(l,x)+path(x,r)) + path(l,r) )/2。
具体实现时,为了方便找到前驱和后继,可以用set维护。

代码

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int bin[30];

int n,m;
set<int> list;

struct E{int y,c,next;}e[maxn*2];int len=0,last[maxn];
void ins(int x,int y,int c)
{
    e[++len]=(E){y,c,last[x]};last[x]=len;
}

int id=0,dfn[maxn];
ll d[maxn];int dep[maxn],f[maxn][30];
void dfs(int x,int fa)
{
    for(int k=last[x];k;k=e[k].next)
    {
        int y=e[k].y;
        if(y==fa) continue;
        dfn[y]=++id;
        dep[dfn[y]]=dep[dfn[x]]+1;
        d[dfn[y]]=d[dfn[x]]+e[k].c;
        f[dfn[y]][0]=dfn[x];
        for(int i=1;i<=20;i++) f[dfn[y]][i]=f[f[dfn[y]][i-1]][i-1];
        dfs(y,x);//debug
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[x]-bin[i]>=dep[y]) x=f[x][i];
//        if(dep[x]-bin[i]<=dep[y]) x=f[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

ll path(int x,int y)
{
    return d[x]+d[y]-2*d[lca(x,y)];//debug d[lca(x,y)]
}

char opt[4];
int main()
{
    bin[0]=1;for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        ins(x,y,c);ins(y,x,c);
    }
    dfn[1]=++id;dfs(1,0);//dfn[1]=1;
    scanf("%d",&m);
    ll ans=0;
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%s",opt);
        if(opt[0]=='+')
        {
            scanf("%d",&x);
            if(list.size()==0) list.insert(dfn[x]);
            else if(list.size()==1)
            {
                ans=2*path(*list.begin(),dfn[x]);
                list.insert(dfn[x]);
            }
            else
            {
                list.insert(dfn[x]);
                set<int>::iterator il=list.find(dfn[x]),ir=il;
                if(ir==list.begin()) ir=list.end();ir--;//debug ir没有-- 
                if(++il==list.end()) il=list.begin();
                ans=ans-path(*il,*ir)+path(*il,dfn[x])+path(dfn[x],*ir);
            }
        }
        else if(opt[0]=='-')
        {
            scanf("%d",&x);
            if(list.size()==1) list.clear();
            else if(list.size()==2)
            {
                ans=0;
                list.erase(dfn[x]);
            }
            else
            {
                set<int>::iterator il=list.find(dfn[x]),ir=il;
                if(ir==list.begin()) ir=list.end();ir--;//debug ir没有-- 
                if(++il==list.end()) il=list.begin();
//                if(ir==list.begin()) ir=list.end(),ir--;//debug 两边一起改 
//                if(++il==list.end()) il=list.begin();
                ans=ans+path(*il,*ir)-path(*il,dfn[x])-path(dfn[x],*ir);
                list.erase(dfn[x]);
            }
        }
        else
        {
            printf("%lld\n",ans/2);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/82919640