CH#56C 异象石

Posted autoint

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CH#56C 异象石相关的知识,希望对你有一定的参考价值。

描述

Adera是Microsoft应用商店中的一款解谜游戏。
异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图。这张地图上有N个点,有N-1条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的M个时刻中,每个时刻会发生以下三种类型的事件之一:
1. 地图的某个点上出现了异象石(已经出现的不会再次出现);
2. 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
3. 向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。
请你作为玩家回答这些问题。

输入格式

第一行有一个整数N,表示点的个数。
接下来N-1行每行三个整数x,y,z,表示点x和y之间有一条长度为z的双向边。
第N+1行有一个正整数M。
接下来M行每行是一个事件,事件是以下三种格式之一:
+ x    表示点x上出现了异象石
- x    表示点x上的异象石被摧毁
?     表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。

输出格式

对于每个 ? 事件,输出一个整数表示答案。

样例输入

6
1 2 1
1 3 5
4 1 7
4 5 3
6 4 2
10
+ 3
+ 1
?
+ 6
?
+ 5
?
- 6
- 3
?

样例输出

5
14
17
10

数据范围与约定

  • 对于30%的数据,1 ≤ n, m ≤ 1000。
  • 对于另20%的数据,地图是一条链,或者一朵菊花。
  • 对于100%的数据,1 ≤ n, m ≤ 10^5, 1 ≤ x, y ≤ n, x ≠ y, 1 ≤ z ≤ 10^9。

题解

把有异象石的点按照其DFS序大小写作一个环形序列,该序列中相邻的数的距离之和就是答案的2倍。

用set维护这个序列,用一个变量记录答案,插入or摧毁时在序列中插入or删除数,并更改答案即可。求距离可以用倍增lca。

时间复杂度\(O((n+m)\log n)\)

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
typedef pair<int,int> pii;

co int N=1e5+1;
int n,m,t,f[N][18],dep[N],dfn[N],tot;
vector<pii> e[N];
ll d[N],ans;
set<pii> st;
typedef set<pii>::iterator it;
void dfs(int x){
    dfn[x]=++tot;
    for(int i=0,y;i<e[x].size();++i){
        if(dep[y=e[x][i].first]) continue;
        dep[y]=dep[x]+1;
        d[y]=d[x]+e[x][i].second;
        f[y][0]=x;
        for(int j=1;j<=t;++j) f[y][j]=f[f[y][j-1]][j-1];
        dfs(y);
    }
}
int lca(int x,int y){
    if(dep[x]>dep[y]) swap(x,y);
    for(int i=t;i>=0;--i)
        if(dep[f[y][i]]>=dep[x]) y=f[y][i];
    if(x==y) return x;
    for(int i=t;i>=0;--i)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
il ll path(int x,int y){
    return d[x]+d[y]-2*d[lca(x,y)];
}
il void insert(int x){
    st.insert(pii(dfn[x],x));
    it l=st.find(pii(dfn[x],x));
    l=l==st.begin()?--st.end():--l;
    it r=st.find(pii(dfn[x],x));
    r=r==--st.end()?st.begin():++r;
    ans-=path(l->second,r->second);
    ans+=path(l->second,x)+path(x,r->second);
}
il void remove(int x){
    it l=st.find(pii(dfn[x],x));
    l=l==st.begin()?--st.end():--l;
    it r=st.find(pii(dfn[x],x));
    r=r==--st.end()?st.begin():++r;
    ans-=path(l->second,x)+path(x,r->second);
    ans+=path(l->second,r->second);
    st.erase(pii(dfn[x],x));
}
int main(){
    read(n);
    for(int i=1,x,y,z;i<n;++i){
        read(x),read(y),read(z);
        e[x].push_back(pii(y,z)),e[y].push_back(pii(x,z));
    }
    t=log(n)/log(2)+1;
    dep[1]=1,dfs(1);
    for(read(m);m--;){
        char s[2];scanf("%s",s);
        if(s[0]=='?') printf("%lld\n",ans/2);
        else{
            int x=read<int>();
            s[0]=='+'?insert(x):remove(x);
        }
    }
    return 0;
}

以上是关于CH#56C 异象石的主要内容,如果未能解决你的问题,请参考以下文章

异象石

Luogu P3320 [SDOI2015]寻宝游戏 / 异象石 LCA/set

异象石

异象石

NOIP模拟赛异象石

异象石/[SDOI2015]寻宝游戏