• SDOI2015 寻宝游戏


    Description

    小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。
    小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。
    为了简化问题,我们认为最开始时所有村庄内均没有宝物。
     

    Input

    第一行,两个整数N、M,其中M为宝物的变动次数。
    接下来的N-1行,每行三个整数x、y、z,表示村庄x、y之间有一条长度为z的道路。
    接下来的M行,每行一个整数t,表示一个宝物变动的操作。若该操作前村庄t内没有宝物,则操作后村庄内有宝物;若该操作前村庄t内有宝物,则操作后村庄内没有宝物。

    Output

    M行,每行一个整数,其中第i行的整数表示第i次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出0。
     

    Sample Input

    4 5
    1 2 30
    2 3 50
    2 4 60
    2
    3
    4
    2
    1

    Sample Output

    0
    100
    220
    220
    280
     

    Data Constraint

     
    解法:
    一个比较直观的想法是把有宝物点的虚树构出来,答案就是虚树中边的总长度*2。
    但动态维护虚树比较麻烦。经过观察我们可以发现:只要出发点在虚树之中,那么答案就不会变,而且走的路径是一个环,即一个经过所有点的欧拉回路。
    即我们把欧拉遍历(或dfn序)中相邻两点的距离加起来,再加上首尾距离,就是答案。
    所以我们用一棵splay维护dfn序,复杂度O(MlogN)
     
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    
    using namespace std;
    
    typedef long long ll;
    
    struct Tree{
        int son[2],fa,key;
    }tr[200011];
    
    ll f[100011],Ans,dep[100011];
    int len[200022],next[200022],y[200022],g[100011],que[100011];
    int fa[100011][17],dfn[100011],num[100011],th[100011],D[100011];
    int n,m,tot,T,j,rs,x,z,q,i,tt,sum,dst,ls,root;
    bool pl;
    
    void star(int i,int j,int k)
    {
        tt++;
        next[tt]=g[i];
        g[i]=tt;
        y[tt]=j;
        len[tt]=k;
    }
    
    void dfs(int x)
    {
        int j,k;
        dfn[x]=++T;
        num[T]=x;
        j=g[x];
        while(j!=0){
            k=y[j];
            if(k!=fa[x][0]){
                fa[k][0]=x;
                D[k]=D[x]+1;
                dep[k]=dep[x]+len[j];
                dfs(k);
            }
            j=next[j];
        }
    }
    
    int get(int x,int z)
    {
        int i,l,e;
        if(D[x]<D[z])swap(x,z);
        l=D[x]-D[z];
        e=0;
        while(l){
            if(l%2==1)x=fa[x][e];
            l/=2;
            e++;
        }
        if(x==z)return x;
        for(i=16;i>=0;i--)if(fa[x][i]!=fa[z][i]){
            x=fa[x][i];
            z=fa[z][i];
        }
        return fa[x][0];
    }
    
    ll dis(int x,int z)
    {
        return dep[x]+dep[z]-2*dep[get(x,z)];
    }
    
    void Find(int x,int z)
    {
        if(tr[x].key==z){
            dst=x;
            return;
        }
        if(tr[x].key>z)Find(tr[x].son[0],z);
        else Find(tr[x].son[1],z);
    }
    
    void ins(int &x,int z,int ls)
    {
        if(x==0){
            x=++tot;
            tr[x].fa=ls;
            tr[x].key=z;
            return;
        }
        if(tr[x].key>z)ins(tr[x].son[0],z,x);
        else ins(tr[x].son[1],z,x);
    }
    
    void rotate(int x)
    {
        int z,e;
        z=tr[x].fa;
        e=tr[z].son[1]==x;
        tr[z].son[e]=tr[x].son[e^1];
        tr[tr[x].son[e^1]].fa=z;
        tr[x].son[e^1]=z;
        if(tr[z].fa!=0){
            e=tr[tr[z].fa].son[1]==z;
            tr[tr[z].fa].son[e]=x;
        }
        tr[x].fa=tr[z].fa;
        tr[z].fa=x;
    }
    
    void splay(int x)
    {
        int nt,ft,e1,e2;
        while(tr[x].fa!=0){
            if(tr[tr[x].fa].fa==0)rotate(x);
            else{
                ft=tr[x].fa;
                nt=tr[ft].fa;
                e1=tr[ft].son[1]==x;
                e2=tr[nt].son[1]==ft;
                if(e1==e2)rotate(ft),rotate(x);
                else rotate(x),rotate(x);
            }
        }
        root=x;
    }
    
    int findlst(int x)
    {
        x=tr[x].son[0];
        while(tr[x].son[1])x=tr[x].son[1];
        return x;
    }
    
    int findnxt(int x)
    {
        x=tr[x].son[1];
        while(tr[x].son[0])x=tr[x].son[0];
        return x;
    }
    
    int findmax(int x)
    {
        while(tr[x].son[1])x=tr[x].son[1];
        return tr[x].key;
    }
    
    int findmin(int x)
    {
        while(tr[x].son[0])x=tr[x].son[0];
        return tr[x].key;
    }
    
    void del(int x)
    {
        Find(root,dfn[x]);
        splay(dst);
        ls=findlst(dst);
        if(!ls)ls=findmax(root);
        else ls=tr[ls].key;
        rs=findnxt(dst);
        if(!rs)rs=findmin(root);
        else rs=tr[rs].key;
        Ans=Ans-dis(num[ls],num[tr[dst].key])-dis(num[rs],num[tr[dst].key]);
        Ans+=dis(num[ls],num[rs]);
        if(findnxt(dst)==0){
            root=tr[dst].son[0];
            tr[root].fa=0;
        }
        else{
            root=findnxt(dst);
            splay(root);
            if(tr[dst].son[0])tr[tr[dst].son[0]].fa=root;
            tr[root].son[0]=tr[dst].son[0];
        }
    }
    
    void Work()
    {
        int i,l,r,x,j,k,lx,mx,mn;
        Ans=0;
        root=0;
        for(i=1;i<=m;i++){
            scanf("%d",&x);
            lx=th[x];
            th[x]^=1;
            if(th[x]==0)del(x);
            else{
                ins(root,dfn[x],0);
                splay(tot);
                ls=findlst(tot);
                if(!ls)ls=findmax(root);
                else ls=tr[ls].key;
                rs=findnxt(tot);
                if(!rs)rs=findmin(root);
                else rs=tr[rs].key;
                Ans-=dis(num[ls],num[rs]);
                Ans+=dis(num[ls],x)+dis(x,num[rs]);
            }
            printf("%lld
    ",Ans);
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        pl=true;
        for(i=1;i<n;i++){
            scanf("%d%d%d",&x,&z,&q);
            star(x,z,q);
            star(z,x,q);
        }
        dfs(1);
        for(i=1;i<=16;i++)
            for(j=1;j<=n;j++)fa[j][i]=fa[fa[j][i-1]][i-1];
        Work();
    }
  • 相关阅读:
    get请求中文乱码问题
    JDBC
    SpringSecurity
    IDEA中创建项目
    Vue路由传参的几种方式
    vue-cli搭建与使用
    docker发布springboot项目
    css伪类的使用
    java实体类序列化与反序列化
    docker网络
  • 原文地址:https://www.cnblogs.com/applejxt/p/4442577.html
Copyright © 2020-2023  润新知