P3320 [SDOI2015] 寻宝游戏

P3320 [SDOI2015] 寻宝游戏

题目描述

小 B 最近正在玩一个寻宝游戏,这个游戏的地图中有 N 个村庄和 N1 条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。

小 B 希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小 B 需要不断地更新数据,但是小 B 太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物。

输入格式

第一行,两个整数 N,M,其中 M 为宝物的变动次数。

接下来的 N1 行,每行三个整数 x,y,z,表示村庄 x,y 之间有一条长度为 z 的道路。

接下来的 M 行,每行一个整数 t,表示一个宝物变动的操作。若该操作前村庄 t 内没有宝物,则操作后村庄内有宝物;若该操作前村庄 t 内有宝物,则操作后村庄内没有宝物。

输出格式

M 行,每行一个整数,其中第 i 行的整数表示第 i 次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出 0

提示

  • 对于 100% 的数据,1N100000, 1M100000, 1z109

Solution:

妙妙树上结论题,我们对一颗树求 dfn ,然后我们维护一个当前所有关键点的序列

那么对于每次修改,我们找到序列上与 pos 相邻的两个点 l,r 假设我们要插入pos 那么产生的贡献将是
disl,pos+dispos,rdisl,r

删除取相反数就好了

然后维护序列的方法有很多,反正我选了抽象 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;
}
posted @   liuboom  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示