P3320 [SDOI2015] 寻宝游戏
P3320 [SDOI2015] 寻宝游戏
题目描述
小 B 最近正在玩一个寻宝游戏,这个游戏的地图中有
小 B 希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小 B 需要不断地更新数据,但是小 B 太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物。
输入格式
第一行,两个整数
接下来的
接下来的
输出格式
0
。
提示
- 对于
的数据, 。
Solution:
妙妙树上结论题,我们对一颗树求
那么对于每次修改,我们找到序列上与
删除取相反数就好了
然后维护序列的方法有很多,反正我选了抽象 set (vector用太多了)
Code:
#include<bits/stdc++.h> #define int long long const int N=1e5+5; const int lg=17; using namespace std; vector<tuple<int,int> >E[N]; set<int> st; set<int>::iterator it; int n,m,ans; int a[N]; int f[N][lg+5],dfn[N],rid[N],dis[N],dep[N]; int col[N]; void dfs(int x,int fa) { dfn[x]=++dfn[0];rid[dfn[0]]=x;dep[x]=dep[fa]+1; f[x][0]=fa; for(int j=1;j<=lg;j++)f[x][j]=f[f[x][j-1]][j-1]; for(auto [y,w] : E[x]){if(y==fa)continue;dis[y]=dis[x]+w;dfs(y,x);} } int LCA(int x,int y) { if(dep[x]<dep[y])swap(x,y); for(int j=lg;j>=0;j--)if(dep[f[x][j]]>=dep[y])x=f[x][j]; if(x==y)return x; for(int j=lg;j>=0;j--)if(f[x][j]!=f[y][j])x=f[x][j],y=f[y][j]; return f[x][0]; } int Dis(int x,int y) { int lca=LCA(x,y); return dis[x]+dis[y]-dis[lca]*2; } void work() { cin>>n>>m; for(int i=1,x,y,w;i<n;i++) { scanf("%lld%lld%lld",&x,&y,&w); E[x].emplace_back(y,w); E[y].emplace_back(x,w); } dfs(1,0); for(int i=1,x,y,z;i<=m;i++) { scanf("%lld",&x); col[x]^=1; if(col[x])st.insert(dfn[x]); y=rid[(it=st.lower_bound(dfn[x]))==st.begin() ? *--st.end() : *--it]; z=rid[(it=st.upper_bound(dfn[x]))==st.end() ? *st.begin() : *it]; if(!col[x])st.erase(dfn[x]); int d=Dis(x,y)+Dis(x,z)-Dis(y,z); ans+= col[x]==1 ? d : -d; printf("%lld\n",ans); } } #undef int int main() { //freopen("P3320.in","r",stdin);freopen("P3320.out","w",stdout); work(); return 0; }