并不是一份合格的题解, 只是提醒一下 set 里可以只存 dfn, 会好写很多。
虽然不是所有人都想不到。
这里用的算法是进阶指南上的算法。
再就是关于算法正确性的问题, 其实要统计的那个东西其实就是对那些点的集合里加上根节点的生成子图进行 dfs 遍历,所经过的边权和;要证明这个东西等于生成子图的边权和,先忽视生成子图里的非叶子非根节点证明正确性,(这个其实就是对dfs遍历的简化) 再证明加上非叶子节点后不会对要维护的这个东西产生影响, 就证完了。
#include<bits/stdc++.h>
using namespace std;
const int N = 100003;
int n,m;
int ct,hd[N],nt[N<<1],vr[N<<1];//存图
long long w[N<<1];//边权
int dfntot, dfn[N], row[N];
// 计数用变量, 时间戳, dfn[row[x]] = x, row[dfn[x]] = x
int dep[N], f[18][N];
// 深度, 根节点的深度记得要设成 1, 倍增lca用数组
long long dis[N];// 点到根节点路径的边权和
long long Ans;
void dfs1(int x,int fa) {
dfn[x] = ++dfntot;
row[dfntot] = x;
f[0][x]=fa;
for(int i=hd[x];i;i=nt[i]) {
int y=vr[i];
if(y==fa) continue;
dep[y]=dep[x]+1;
dis[y] = 0ll + dis[x] + w[i];
dfs1(y,x);
}
}
void ad(int x,int y,int z) {
vr[++ct]=y, w[ct]=0ll+z, nt[ct]=hd[x], hd[x]=ct;
}
set<int>s;
// set 里存的是dfn, row[dfn] 是对应的节点
int lca(int x,int y) {
if(dep[x]>dep[y]) swap(x,y);
for(int k=17;k>=0;--k)
if(dep[f[k][y]] >= dep[x]) y=f[k][y];
if(x==y) return x;
for(int k=17;k>=0;--k)
if(f[k][x] != f[k][y]) x=f[k][x],y=f[k][y];
return f[0][x];
}
long long d(int x,int y) {
return 0ll+dis[x]+dis[y]-2*dis[lca(x,y)];
}
void ins(int x) {
if(!s.size()) {
s.insert(dfn[x]);
return;
}
set<int>::iterator nxt = s.lower_bound(dfn[x]);
set<int>::iterator pre;
if(nxt==s.begin()) {pre=s.end();--pre;}
else {pre=nxt;--pre;}
if(nxt==s.end()) nxt=s.begin();
int l=row[*pre], r=row[*nxt];
Ans -= d(l,r);
Ans += d(l,x)+d(x,r);
s.insert(dfn[x]);
}
void del(int x) {
set<int>::iterator ths = s.lower_bound(dfn[x]);
set<int>::iterator pre, nxt;
if(ths==s.begin()) {pre=s.end();--pre;}
else {pre=ths;--pre;}
nxt=ths; ++nxt;
if(nxt==s.end()) nxt=s.begin();
int l=row[*pre], r=row[*nxt];
Ans -= d(l,x)+d(x,r);
Ans += d(l,r);
s.erase(dfn[x]);
}
int main() {
cin>>n;
for(int i=1;i<n;++i) {int x,y,z;scanf("%d%d%d", &x,&y,&z);ad(x,y,z);ad(y,x,z);}
dep[1]=1;
dfs1(1,0);
for(int k=1;k<=17;++k)
for(int i=1;i<=n;++i)
f[k][i] = f[k-1][f[k-1][i]];
cin>>m;
while(m--) {
char s[3];
int x;
scanf("%s",s);
if(s[0]=='?') {
cout << Ans/2 << '\n';
} else {
scanf("%d",&x);
if(s[0]=='+') {
ins(x);
} else {
del(x);
}
}
}
return 0;
}
吐槽一下, tab 缩进换成 2 空格真的会让不少代码看起来更漂亮(至少是我的代码)