题目大意:给出一棵树,每个边都有一个权值,现在有一个空的集合,两种操作,1 x吧x节点放到集合中(如果还没放入),2 x把x节点从集合中拿出来(已放入)。每次操作后输出最小的边权和,保证这些边可以将这些点连起来。
首先明确一点的是,两个点x,y最短的路径肯定是经过两点的lca的,而新添一个点u之后,也就是这个点要连到这条链上去的话,是有下面的公式的:
首先是u到x的距离,dis[u]+dis[x]-2*dis[lca(x,u)]
u到y的距离,dis[u]+dis[x]-2*dis[lca(x,u)]
x到y的距离,dis[x]+dis[y]-2*dis[lca(x,y)]
u到这条链的距离就是u到x的距离+u到y的距离-x到y的距离,结果除以2.
那么我们知道了边权和是怎么增加的了,下面就是要找该点u究竟连到那条链上能最短。
题解上用到了dfs序,让我证明我不会,但是画图的话是能够明白这种做法是正确的。即如果u点dfs序被集合点的dfs序夹住了,那么找最靠近u的两个点,恩,是dfs序最靠近u的。如果没被夹住,就找集合的最大和最小的dfs序对应的点。套上面的公式即可。
不明白怎么回事的自己画棵树比划比划就行吧。
自己写的模板太烂了,直接爆栈了,网上找了一模板自己又写了一边,屯一下。思路模板都不是我的,真是弱爆了。
代码:
//3024ms lca倍增法
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<set>
#include<queue>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int maxn=100005;
struct node{
int v,next,c;
};
node edge[maxn<<1];
int head[maxn],cnt;
void add(int a,int b,int c){
edge[cnt].v=b;
edge[cnt].c=c;
edge[cnt].next=head[a];
head[a]=cnt++;
}
int belong[maxn];
int first[maxn];//第i个节点最早出现的时候
int time1;
int vis[maxn];
set<int>s;
set<int>::iterator it,itx;
int dis[maxn], fa[maxn][20], dep[maxn];
void bfs(int root) {
queue<int> q;
fa[root][0] = root;dep[root] = 0;dis[root] = 0;
q.push(root);
while (!q.empty()) {
int u = q.front();q.pop();
for (int i = 1;i<20;i++)fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int i = head[u]; ~i;i = edge[i].next) {
int v = edge[i].v;if (v == fa[u][0])continue;
dep[v] = dep[u] + 1;dis[v] = dis[u] + edge[i].c;fa[v][0] = u;
q.push(v);
}
}
}
int Lca(int x, int y) {
if (dep[x]<dep[y])swap(x, y);
for (int i = 0;i<20;i++)if ((dep[x] - dep[y])&(1 << i))x = fa[x][i];
if (x == y)return x;
for (int i = 19;i >= 0;i--)if (fa[x][i] != fa[y][i])x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
void dfs(int u, int fa) {
belong[u] = ++time1;
first[time1] = u;
for (int i(head[u]); ~i; i = edge[i].next) {
int v = edge[i].v; if (v == fa)continue;
dfs(v, u);
}
}
void init(){
memset(head,-1,sizeof(head));
cnt=0;
memset(first,0,sizeof(first));
memset(vis,0,sizeof(vis));
}
int solve(int u){
if (s.empty())
return 0;
int x, y;
set<int>::iterator it = s.lower_bound(u), itx = it;
itx--;
if (it == s.end() || it == s.begin()) {
it = s.begin();
itx = s.end();
itx--;
}
y = (*it);
x = (*itx);
y = first[y];
x =first[x];
u=first[u];
return dis[u]-dis[Lca(u,x)]-dis[Lca(u,y)]+dis[Lca(x,y)];
}
int main(){
int n,q;
int u,v,d;
int T;
int cas=1;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&q);
init();
s.clear();
for(int i=1;i<n;i++){
scanf("%d%d%d",&u,&v,&d);
add(u,v,d);
add(v,u,d);
}
bfs(1);
time1=0;
dfs(1,1);
int ans=0;
printf("Case #%d:\n",cas++);
for(int i=1;i<=q;i++){
scanf("%d%d",&u,&v);
v=belong[v];
if(u==1){
if(!vis[v]){
vis[v]=1;
if(s.size()==0){
s.insert(v);
}
else{
ans+=solve(v);
s.insert(v);
}
}
}
else{
if(vis[v]){
vis[v]=0;
s.erase(v);
if(s.size()!=0){
ans-=solve(v);
}
}
}
printf("%d\n",ans);
}
}
return 0;
}