zoukankan      html  css  js  c++  java
  • HYSBZ 3991 寻宝游戏

    题意:
    小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物
    题解:
    ①有一个比较常见的结论:
    从一个点出发,经过指定的k个点再回到原点走的路径的长度是这k个点构成的生成树的边权和的两倍
    ②这道题如果把有宝藏的点当作特殊点,就相当于维护m棵虚树.
    ③每次重新建虚树是肯定不行的,每次只会修改一个点,插入和删除这个点一定会伴随着若干条路径的改变,找到这些路径就好了
    ④画图分析可以知道:
    这些路径一定是当前操作的点x和DFS序在它前面的最后一个点L组成的
    或者是和DFS序在它后面的第一个点R组成的
    ⑴如果L存在,那么这条路径就是L→x
    ⑵如果R存在路径R→x到x也会受到影响
    ⑶如果L和R同时存在,还要排除L→R的影响
    ⑷ans还要加上tmp是因为直接加dis会使DFS序最小的点x到DFS序最大的点y这条边x→y只算了一次,要加上另一次
    ⑤只需要记住每次加点还是删点都会改变若干条路径对答案的贡献,找到这些路径并修改贡献就好了.
    ⑥因为要每次要插入一个DFS序,找前驱后继,所以用set来维护

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    using namespace std;
    typedef long long LL;
    const int maxn=100000+10;
    struct Node{
            int to,next,data;
    }a[maxn<<1];
    int n,m,head[maxn],cnt;
    int prt[maxn],deep[maxn],size[maxn],son[maxn];
    LL dis[maxn];
    int Dfn[maxn],Time,top[maxn],pos[maxn];
    bool flag[maxn];
    LL ans=0;
    set<int>S;
    void Init();
    void Insert(int,int,int);
    void DFS1(int,int,int);
    void DFS2(int,int);
    int LCA(int,int);
    LL Dis(int,int);
    void Solve_Insert(int);
    void Solve_Delte(int);
    int main(){
            // freopen("in.cpp","r",stdin);
            Init();
            return 0;
    }
    void Init(){
            scanf("%d%d",&n,&m);
            int x,y,z;
            for(int i=1;i<n;i++){
                    scanf("%d%d%d",&x,&y,&z);
                    Insert(x,y,z);Insert(y,x,z);
            }
            DFS1(1,0,1);
            DFS2(1,1);
            S.insert(0);S.insert(n+1);
            while(m--){
                    scanf("%d",&x);
                    flag[x]=!flag[x];
                    if(flag[x])Solve_Insert(x);
                    else Solve_Delte(x);
            }
    }
    void Solve_Insert(int x){
            int L,R,prt1,prt2;LL tmp=0;
            S.insert(Dfn[x]);
            L=*--S.find(Dfn[x]);R=*++S.find(Dfn[x]);
            if(L>=1)ans+=Dis(pos[L],x);
            if(R<=n)ans+=Dis(pos[R],x);
            if(L>=1 && R<=n)ans-=Dis(pos[L],pos[R]);
            L=*++S.find(0),R=*--S.find(n+1);
            if(L>=1 && R<=n)
                    tmp=Dis(pos[L],pos[R]);
            printf("%lld
    ",tmp+ans);
    }
    void Solve_Delte(int x){
            int L,R,prt1,prt2;LL tmp=0;
            L=*--S.find(Dfn[x]);R=*++S.find(Dfn[x]);
            if(L>=1)ans-=Dis(pos[L],x);
            if(R<=n)ans-=Dis(pos[R],x);
            if(L>=1 && R<=n)ans+=Dis(pos[L],pos[R]);
            S.erase(Dfn[x]);
            L=*++S.find(0),R=*--S.find(n+1);
            if(L>=1 && R<=n)
                    tmp=Dis(pos[L],pos[R]);
            printf("%lld
    ",tmp+ans);
    }
    void Insert(int x,int y,int z){
            a[++cnt].to=y;
            a[cnt].next=head[x];
            a[cnt].data=z;
            head[x]=cnt;
    }
    #define y a[i].to
    void DFS1(int x,int prt,int deep){
            ::deep[x]=deep;
            ::prt[x]=prt;
            ::size[x]=1;
            for(int i=head[x];i;i=a[i].next){
                    if(y==prt)continue;
                    dis[y]=dis[x]+a[i].data;
                    DFS1(y,x,deep+1);
                    size[x]+=size[y];
                    if(size[y]>size[son[x]])son[x]=y;
            }
    }
    void DFS2(int x,int top){
            ::Dfn[x]=++Time;
            ::top[x]=top;
            ::pos[Time]=x;
            if(son[x])DFS2(son[x],top);
            for(int i=head[x];i;i=a[i].next){
                    if(y==prt[x] || y==son[x])continue;
                    DFS2(y,y);
            }
    }
    #undef y
    int LCA(int x,int y){
            while(top[x]!=top[y]){
                    if(deep[top[x]]<deep[top[y]])swap(x,y);
                    x=prt[top[x]];
            }
            if(deep[x]<deep[y])return x;
            return y;
    }
    LL Dis(int x,int y){
            return dis[x]+dis[y]-2*dis[LCA(x,y)];
    }

     

  • 相关阅读:
    Python中的list和tuple
    Python中输出格式化的字符串
    Python笔记-第一天
    在Lingo中输入矩阵(通过Excel)
    将Matlab中的矩阵输出到txt文件
    SQL中对于两个不同的表中的属性取差集except运算
    SQL中union运算操作的理解
    SQL笔记----在一个关系表中操作列
    MathType的公式在word中跟文字不对齐
    开发android过程中eclipse闪退解决
  • 原文地址:https://www.cnblogs.com/holy-unicorn/p/9510293.html
Copyright © 2011-2022 走看看