1 条题解
-
0
自动搬运
来自洛谷,原作者为

ivyjiao
复活了搬运于
2025-08-24 23:02:37,当前版本为作者最后更新于2024-08-25 09:36:12,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
不是,哥们,你真来啊。
双倍经验:P3320 [SDOI2015] 寻宝游戏。
三倍经验:CF176E Archaeology。
我们先看 P3320。
结论很好猜,模拟一下走的路径就明白了:
假设下图中, 号节点有宝物。

红色路径就是我们来时走过的路径。如果我们算上返回时走的路径,那么我们经过的路径经过且仅经过两次。
此时
根据直觉,能知道这条路径与 LCA 有关,于是我们先敲一个板子。void dfs(int u,int fa,int w){ dep[u]=dep[fa]+1; dis[u]=dis[fa]+w; f[u][0]=fa; for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1]; for(int i=0;i<G[u].size();i++){ int v=G[u][i].se,w=G[u][i].fi; if(v==fa) continue; dfs(v,u,w); } } int lca(int x,int y){ if(x==y) return x; if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--){ if(dep[f[x][i]]>=dep[y]) x=f[x][i]; } if(x==y) return x; for(int i=20;i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i]; y=f[y][i]; } } return f[x][0]; } int getdis(int x,int y){ return dis[x]+dis[y]-2*dis[lca(x,y)]; }然后进一步分析。我们先假设只有 一对点(单程):

我们发现,结合 LCA,从 到 的路径长度可以表示为 $dis_4+dis_5-2\times dis_{2}=dis_4+dis_5-2\times dis_{lca_{4,5}}$。
对于 , 两对点,也是如此。
但是我们还发现,如果把宝物点的数量继续增多,那么点对的顺序将不能随意变化。模拟之后,我们发现点对的顺序仅仅和点的 dfs 序有关,且最终 个点组成 个点对,拆开后变成一个环(因为最终要回到最开始的点)。实际上,原图点的顺序就是 dfs 序。
我们再假设原图中 号点也有宝物,那么 个点对分别是 。路径如下(往返):

设 , 为字典序为第 大的宝物点,则 $ans=dist_{a_1,a_2}+dist_{a_2,a_3}+\cdots+dist_{a_{n-1},a_n}+dist_{a_n,a_1}$。动态维护 即可。
dfs 序的顺序是很好维护的,用 set 维护即可,也可以用 rb_tree_tag。
对于本题,由于我们经过的路径经过且仅经过两次,而本题每条路径只需要算一遍,所以直接将答案 即可。
代码如下:
#include<bits/stdc++.h> #define PII pair<int,int> #define fi first #define se second #define int long long using namespace std; int n,m,x,y,z,t,dep[100001],dis[100001],dfn[100001],f[100001][21],sum,ans; char op; bool vis[100001]; vector<PII>G[100001]; set<PII>st; void dfs(int u,int fa,int w){ dep[u]=dep[fa]+1; dis[u]=dis[fa]+w; dfn[u]=++sum; f[u][0]=fa; for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1]; for(int i=0;i<G[u].size();i++){ int v=G[u][i].se,w=G[u][i].fi; if(v==fa) continue; dfs(v,u,w); } } int lca(int x,int y){ if(x==y) return x; if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--){ if(dep[f[x][i]]>=dep[y]) x=f[x][i]; } if(x==y) return x; for(int i=20;i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i]; y=f[y][i]; } } return f[x][0]; } int getdis(int x,int y){ return dis[x]+dis[y]-2*dis[lca(x,y)]; } signed main(){ cin>>n; for(int i=1;i<n;i++){ cin>>x>>y>>z; G[x].push_back({z,y}); G[y].push_back({z,x}); } dfs(1,0,0); cin>>m; while(m--){ cin>>op; if(op=='+'||op=='-'){ cin>>t; vis[t]=!vis[t]; if(vis[t]) st.insert({dfn[t],t}); auto it1=st.lower_bound({dfn[t],t}),it2=st.upper_bound({dfn[t],t}); int a=(it1==st.begin()? (*--st.end()).se:(*--it1).se); int b=(it2==st.end()? (*st.begin()).se:(*it2).se); swap(a,b); if(!vis[t]) st.erase({dfn[t],t}); int d=getdis(t,a)+getdis(t,b)-getdis(a,b); if(vis[t]) ans+=d; else ans-=d; } else cout<<ans/2<<endl; } }
- 1
信息
- ID
- 10190
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 0
- 已通过
- 0
- 上传者