P3320 [SDOI2015]寻宝游戏(set+LCA)
题意
一棵 个节点的树,边有边权。
每个点可能是关键点,每次操作改变一个点是否是关键点。
求所有关键点形成的极小连通子树的边权和的两倍。
输入格式
第一行,两个整数 N、M,其中 M 为宝物的变动次数。
接下来的 N-1 行,每行三个整数 x、y、z,表示村庄 x、y 之间有一条长度为 z 的道路。
接下来的 M 行,每行一个整数 t,表示一个宝物变动的操作。若该操作前村庄 tt 内没有宝物,则操作后村庄内有宝物;若该操作前村庄 t 内有宝物,则操作后村庄内没有宝物。
输出格式
M 行,每行一个整数,其中第 i 行的整数表示第 i 次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出 0.
数据范围
,
,
样例
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排序后是{}。
那么所有关键点形成的极小联通子树的边权和的两倍等于++...++。
那么求一下 DFS 序,每次操作相当于往集合里加入/删除一个元素。
假设插入 ,它DFS序左右两边分别是 和 。那么答案加上 即可。
删除同理。
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!