zoukankan      html  css  js  c++  java
  • bzoj3991 LCA + set

    https://www.lydsy.com/JudgeOnline/problem.php?id=3991

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

    很显然问题的关键在于处理每一次变更的宝藏点的信息,一个容易发现的结论是,只要是从宝藏点出发,无论哪个点出发都能寻找到一条最优的路径,因为行走的路径会是一个环,对于K个宝藏点,行走的路径是1->2,2->3,3->4.........k-1 -> k,k ->1,所以说,对于一条链,每一次变更只要找到这个点插入的前驱pre和后继nxt,插入的时候删除dis(pre,nxt),加上dis(pre,x) + dis(x,pre)就可以了,删除同理。

    问题在于怎么去寻找他的前驱和后继,对于一棵树来说,想要遍历所有的关键点,贪心的想到是和dfs一样走,可以最短的经过一圈所有的关键点,所以我们掏出一手dfs序的前序,对于每一个数字的更改,去寻找他在dfs序里面前后最接近的宝藏点就是他的前驱和后继。

    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Sca2(x,y) scanf("%d%d",&x,&y)
    #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
    #define Scl(x) scanf("%lld",&x);  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x);  
    #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
    #define LL long long
    #define ULL unsigned long long  
    #define mp make_pair
    #define PII pair<int,int>
    #define PIL pair<int,long long>
    #define PLL pair<long long,long long>
    #define pb push_back
    #define fi first
    #define se second 
    typedef vector<int> VI;
    const double eps = 1e-9;
    const int maxn = 1e5 + 10;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7; 
    const int SP = 20;
    int N,M,K;
    struct Edge{
        int to,next;
        LL dis;
    }edge[maxn * 2];
    int dfn[maxn];
    int head[maxn],tot;
    int id;
    int idx[maxn];
    LL Dis[maxn];
    int pa[maxn][SP],dep[maxn];
    int vis[maxn];
    set<int>Q;
    void init(){
        Mem(head,-1);
        tot = 0;
    }
    void add(int u,int v,LL w){
        edge[tot].to = v;
        edge[tot].next = head[u];
        edge[tot].dis = w;
        head[u] = tot++;
    }
    void dfs(int u,int la){
        dfn[u] = ++id; idx[id] = u;
        pa[u][0] = la;
        For(i,1,SP - 1) pa[u][i] = pa[pa[u][i - 1]][i - 1];
        for(int i = head[u]; ~i; i = edge[i].next){
            int v = edge[i].to;
            if(v == la) continue;
            dep[v] = dep[u] + 1;
            Dis[v] = Dis[u] + edge[i].dis;
            dfs(v,u);
        }
    }
    int lca(int u,int v){
        if(dep[u] < dep[v]) swap(u,v);
        int t = dep[u] - dep[v];
        For(i,0,SP - 1) if(t & (1 << i)) u = pa[u][i];
        _For(i,SP - 1,0){
            int uu = pa[u][i],vv = pa[v][i];
            if(uu != vv){
                u = uu;
                v = vv;
            }
        }
        return u == v?u:pa[u][0];
    }
    LL DIS(int u,int v){
        return Dis[u] + Dis[v] - 2 * Dis[lca(u,v)];
    }
    int main()
    {
        Sca2(N,M); init();
        For(i,1,N - 1){
            int u,v; Sca2(u,v);
            LL w; Scl(w);
            add(u,v,w); add(v,u,w);
        }
        Dis[1] = 0;id = 0;dfs(1,1);
        LL ans = 0;
        Q.insert(0); Q.insert(N + 1);
        while(M--){
            int x; Sca(x);
            if(vis[x]){
                int pre = *--Q.find(dfn[x]),pro = *++Q.find(dfn[x]);
                if(pre >= 1) ans -= DIS(idx[pre],x);
                if(pro <= N) ans -= DIS(idx[pro],x);
                if(pre >= 1 && pro <= N) ans += DIS(idx[pre],idx[pro]);
                Q.erase(dfn[x]);
            }else{
                Q.insert(dfn[x]);
                int pre = *--Q.find(dfn[x]),pro = *++Q.find(dfn[x]);
                if(pre >= 1) ans += DIS(idx[pre],x);
                if(pro <= N) ans += DIS(idx[pro],x);
                if(pre >= 1 && pro <= N) ans -= DIS(idx[pre],idx[pro]);
            }
            LL z = 0;
            int s = *++Q.find(0),e = *--Q.find(N + 1);
            if(s >= 1 && e <= N) z = DIS(idx[s],idx[e]);
            Prl(ans + z);
            vis[x] ^= 1;
        }
        #ifdef VSCode
        system("pause");
        #endif
        return 0;
    }
  • 相关阅读:
    redis 实例
    redis 常用命令
    redis sets类型及操作
    简单说说PHP优化那些事
    C# IEnumerable与IQueryable ,IEnumerable与IList ,LINQ理解Var和IEnumerable
    全文搜索引擎 elasticsearch.net
    .net 异步
    并行开发 8.用VS性能向导解剖你的程序
    并行开发 7.简要分析任务与线程池
    并行开发 6.异步编程模型
  • 原文地址:https://www.cnblogs.com/Hugh-Locke/p/9762335.html
Copyright © 2011-2022 走看看