一本通1554【例 3】异象石
1554:【例 3】异象石
时间限制: 1000 ms 内存限制: 524288 KB
题目描述
在 Adera 的异时空中有一张地图。这张地图上有 N 个点,有 N−1 条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的 M 个时刻中,每个时刻会发生以下三种类型的事件之一:
- 地图的某个点上出现了异象石(已经出现的不会再次出现);
- 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
- 向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。
请你作为玩家回答这些问题。下图是一个例子,灰色节点表示出现了异象石,加粗的边表示被选为连通异象石的边集。
输入格式
第一行有一个整数 N,表示点的个数;
接下来 N−1 行每行三个整数 x,y,z,表示点 x 和 y 之间有一条长度为 z 的双向边;
第 N+1 行有一个正整数 M;
接下来 M 行每行是一个事件,事件是以下三种格式之一:
+ x
:表示点 x 上出现了异象石;- x
:表示点 x 上的异象石被摧毁;?
:表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。
输出格式
对于每个 ?
事件,输出一个整数表示答案。
样例
样例输入
6
1 2 1
1 3 5
4 1 7
4 5 3
6 4 2
10
+ 3
+ 1
?
+ 6
?
+ 5
?
- 6
- 3
?
样例输出
5
14
17
10
数据范围与提示
对于 30% 的数据,1≤ n,m ≤ 10^3;
对于另 20% 的数据,地图是一条链,或者一朵菊花;
对于 100% 的数据,1≤ n,m ≤ 10^5,1 ≤ x,y ≤ n,x !=y,1 ≤ z ≤ 10^9。
sol:先考虑下给你一堆点怎么求把他们全部连起来需要的费用,发现就是从一个点以任意顺序依次遍历每个点再回到出发点的边权和的1/2,之后就会容易一点,假定这是以dfs遍历的,弄一个set,加点删点是对应的加上几条路径或减去几条就可以了
#include <bits/stdc++.h> using namespace std; const int N=100005,M=200005,inf=0x3f3f3f3f; int n,m; struct Tree { int tot,Next[M],to[M],val[M],head[N]; inline void add(int x,int y,int z) { Next[++tot]=head[x]; to[tot]=y; val[tot]=z; head[x]=tot; return; } int Id_cnt,Id[N],Xuhao[N]; //dfs序 bool Inset[N]; int Depth[N],F[N][23]; long long Dis[N]; inline void dfs(int x,int fa) { Id[++Id_cnt]=x; Xuhao[x]=Id_cnt; F[x][0]=fa; Depth[x]=Depth[fa]+1; int i; for(i=head[x];i;i=Next[i]) if(to[i]!=fa) { Dis[to[i]]=Dis[x]+val[i]; dfs(to[i],x); } return; } set<int>Set; inline void Pre() { int i,j; dfs(1,0); for(i=1;i<=19;i++) { for(j=1;j<=n;j++) F[j][i]=F[F[j][i-1]][i-1]; } Set.insert(-inf); Set.insert(inf); return; } inline int Ask_Lca(int x,int y) { int i; if(Depth[x]<Depth[y]) swap(x,y); for(i=19;~i;i--) if(Depth[F[x][i]]>=Depth[y]) { x=F[x][i]; } if(x==y) return x; for(i=19;~i;i--) if(F[x][i]!=F[y][i]) { x=F[x][i]; y=F[y][i]; } return F[x][0]; } inline long long Ask_Dis(int x,int y) { return Dis[x]+Dis[y]-(Dis[Ask_Lca(x,y)]<<1); } long long Dis_All; inline void Point_Add(int x) { if(Inset[Xuhao[x]]) return; Set.insert(Xuhao[x]); Inset[Xuhao[x]]=1; set<int>::iterator it=Set.upper_bound(Xuhao[x]); int p2=(*it),p1=(*(--(--it))); // printf("%d %d\n",p1,p2); // puts("EEE"); if(p1!=-inf) Dis_All+=Ask_Dis(Id[p1],x); if(p2!=inf) Dis_All+=Ask_Dis(x,Id[p2]); // puts("FFF"); if((p1!=-inf)&&(p2!=inf)) Dis_All-=Ask_Dis(Id[p1],Id[p2]); return; } inline void Point_Del(int x) { if(!Inset[Xuhao[x]]) return; Set.erase(Xuhao[x]); Inset[Xuhao[x]]=0; set<int>::iterator it=Set.upper_bound(Xuhao[x]); int p2=(*it),p1=(*(--it)); if(p1!=-inf) Dis_All-=Ask_Dis(Id[p1],x); if(p2!=inf) Dis_All-=Ask_Dis(x,Id[p2]); if((p1!=-inf)&&(p2!=inf)) Dis_All+=Ask_Dis(Id[p1],Id[p2]); return; } inline long long Get_ans() { long long DD=(Set.size()>3)?(Ask_Dis(Id[*(Set.upper_bound(-inf))],Id[*(--Set.lower_bound(inf))])):0; return (Dis_All+DD)>>1; } inline void Init() { tot=Dis_All=Id_cnt=0; memset(head,0,sizeof head); memset(Inset,0,sizeof Inset); return; } }T; int main() { // freopen("stone6.in","r",stdin); // freopen("my.out","w",stdout); int i; scanf("%d",&n); for(i=1;i<n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); T.add(x,y,z); T.add(y,x,z); } T.Pre(); scanf("%d",&m); for(i=1;i<=m;i++) { char Ch[5]; int x; scanf("%s",Ch+1); switch (Ch[1]) { case '+': scanf("%d",&x); T.Point_Add(x); break; case '-': scanf("%d",&x); T.Point_Del(x); break; case '?': printf("%lld\n",T.Get_ans()); break; default: break; } } return 0; } /* input 6 1 2 1 1 3 5 4 1 7 4 5 3 6 4 2 10 + 3 + 1 ? + 6 ? + 5 ? - 6 - 3 ? output 5 14 17 10 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!