1. 程式人生 > 其它 >P3320 [SDOI2015]尋寶遊戲(set+LCA)

P3320 [SDOI2015]尋寶遊戲(set+LCA)

題目傳送門

題意

一棵 \(n\) 個節點的樹,邊有邊權。
每個點可能是關鍵點,每次操作改變一個點是否是關鍵點。
求所有關鍵點形成的極小連通子樹的邊權和的兩倍。

輸入格式

第一行,兩個整數 N、M,其中 M 為寶物的變動次數。
接下來的 N-1 行,每行三個整數 x、y、z,表示村莊 x、y 之間有一條長度為 z 的道路。
接下來的 M 行,每行一個整數 t,表示一個寶物變動的操作。若該操作前村莊 tt 內沒有寶物,則操作後村莊內有寶物;若該操作前村莊 t 內有寶物,則操作後村莊內沒有寶物。

輸出格式

M 行,每行一個整數,其中第 i 行的整數表示第 i 次操作之後玩家找到所有寶物需要行走的最短路程。若只有一個村莊內有寶物,或者所有村莊內都沒有寶物,則輸出 0.

資料範圍

\(1 \leq N \leq 100000\),
\(\ 1 \leq M \leq 100000\),
\(\ 1 \leq z \leq 10^9\)

樣例

input

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

output

0
100
220
220
280

思路

DFS序求出之後,關鍵點按DFS排序後是{\({a_1,a_2,...,a_k}\)}。
那麼所有關鍵點形成的極小聯通子樹的邊權和的兩倍等於\(dis(a_1,a_2)\)+\(dis(a_2,a_3)\)+...+\(dis(a_{k-1},a_k)\)+\(dis(a_k,a_1)\)
那麼求一下 DFS 序,每次操作相當於往集合里加入/刪除一個元素。

假設插入 \(x\),它DFS序左右兩邊分別是 \(y\)\(z\)。那麼答案加上 \(\mathrm{dis}(x,y)+\mathrm{dis}(x,z)-\mathrm{dis}(y,z)\) 即可。

刪除同理。

code

#include <bits/stdc++.h>
using  namespace  std;

typedef long long ll;
typedef unsigned long long ull;
//#pragma GCC optimize(3)
#define pb push_back
#define is insert
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define show(x) cerr<<#x<<" : "<<x<<endl;
//mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
//ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}

const int INF=0x3f3f3f3f;//2147483647;
const int N=1e5+50,M=1e5+50;
const ll mod=998244353;

int n,m;
int head[N];
int tot=0;
struct node {
    int to,nxt;ll val;
}e[M<<1];
void add_edge(int u,int v,ll val){
    e[tot].to=v,e[tot].nxt=head[u],e[tot].val=val,head[u]=tot++;
}

int rt;
int depth[N];
int fa[N][18];
ll dis[N];
queue<int>q;
int dfn[N],idf[N],idx=0;
void dfs(int x,ll step,int pre){
    dfn[x]=++idx;idf[idx]=x;
    dis[x]=step;
    for(int i=head[x];~i;i=e[i].nxt){
        if(e[i].to==pre){
            continue;
        }
        dfs(e[i].to,step+e[i].val,x);
    }
}
void init_bfs(){
    for(int i=1;i<=n;i++){
        depth[i]=INF;
    }
    depth[0]=0;
    depth[rt]=1;
    fa[rt][0]=0;
    q.push(rt);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=e[i].nxt){
            int v=e[i].to;
            if(depth[v]>depth[u]+1){
                depth[v]=depth[u]+1;
                fa[v][0]=u;
                q.push(v);
                for(int k=1;k<=17;k++){
                    fa[v][k]=fa[fa[v][k-1]][k-1];
                }
            }
        }
    }
}
int lca(int u,int v){
    if(depth[u]<depth[v])swap(u,v);
    for(int k=17;k>=0;k--){
        if(depth[fa[u][k]]>=depth[v]){
            u=fa[u][k];
        }
    }
    if(u==v){
        return u;
    }
    for(int k=17;k>=0;k--){
        if(fa[u][k]!=fa[v][k]){
            u=fa[u][k];
            v=fa[v][k];
        }
    }
    return fa[u][0];
}
set<int>s;
int vis[N];
int cnt=0;
ll getdis(int u,int v){
    int tmp=lca(u,v);
    return dis[u]+dis[v]-dis[tmp]*2;
}
void solve() {
    memset(head,-1,sizeof head);
    cin>>n>>m;
    for(int i=1;i<=n-1;i++){
        int u,v,val;cin>>u>>v>>val;
        add_edge(u,v,val);
        add_edge(v,u,val);
    }
    rt=1;
    dfs(rt,0,-1);
    init_bfs();
    ll ans=0;
//    for(int i=1;i<=n;i++){
//        cout<<dfn[i]<<" ";
//    }cout<<endl;
    for(int i=1;i<=m;i++){
        int tmp;cin>>tmp;
        tmp=dfn[tmp];
        if(!vis[tmp]) s.is(tmp);
        auto it = s.lower_bound(tmp);
        auto itt = s.upper_bound(tmp);
        int y, z;
        if (it == s.begin()) {
            y = (*--s.end());
        } else {
            y = (*--it);
        }
        if (itt == s.end()) {
            z = (*s.begin());
        } else {
            z = (*itt);
        }
        ll delta=getdis(idf[tmp], idf[y]) + getdis(idf[tmp], idf[z]) - getdis(idf[y], idf[z]);
        if(vis[tmp]){
            s.erase(tmp);
            ans-=delta;
            vis[tmp]=0;
        }
        else {
            vis[tmp]=1;
            ans+=delta;
        }
        cout<<ans<<"\n";
    }
}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int __=1;//cin>>__;
    while(__--){
        solve();
    }
    return 0;
}