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;
}
原文地址:https://www.cnblogs.com/Hugh-Locke/p/9762335.html