15年多校第一场的题。比赛的时候没想出来,看了题解算是豁然开朗。首先对这棵树预处理出DFS序。对集合的操作相当于构造了一棵新的树。
首先我们考虑插入操作。在已有的集合里寻找DFS序比操作点u大的最小点y以及小于操作点的最大点x。我们可以得到一个结论,联通这三点的路径是必须要加在新构造的树上的。(从x访问到y之间的路上不会出现集合上的点,加入u后,从x到u,u到y的路上也同样不会有集合上的点)所以我们只需要在现有的答案上加上u点到xy链的距离即可。这个距离就要通过求LCA来计算,公式为dir[u]-dir[lca(x,u)]-dir[lca(y,u)]+dir[lca(x,y)]
当找不到这样的两个点时,说明操作点的DFS序为最大或者最小。故我们只需要取出集合中DFS序最大和最小的两个点,同上来求即可。
删除时同理。注意在集合操作时,若添加则在添加前查找,删除则在删除后查找。
代码中LCA使用倍增算法。
/*************************************************************************
> File Name: main.cpp
> Author: znl1087
> Mail: loveCJNforever@gmail.com
> Created Time: 日 7/26 13:46:25 2015
************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
using namespace std;
int n,q;
const int MAXN = 100005;
int head[MAXN],cnt;
int dir[MAXN],p[MAXN][20],dep[MAXN],tme[MAXN],t;
int dfn[MAXN];
struct edge{
int u,v,w,next;
edge(){}
edge(int u,int v,int w):u(u),v(v),w(w){}
}e[MAXN<<1];
struct point{
int id;
point(){};
point(int id):id(id){}
bool operator < (const point & b) const{
return dfn[id] < dfn[b.id];
}
};
set<point> se;
void addedge(int u,int v,int w){
e[cnt] = edge(u,v,w),e[cnt].next = head[u];
head[u] = cnt++;
e[cnt] = edge(v,u,w),e[cnt].next = head[v];
head[v] = cnt++;
}
void dfs(int u){
tme[t++] = u;
for(int i = head[u];i!=-1;i = e[i].next){
int v = e[i].v;
if(!dep[v]){
dir[v] = dir[u] + e[i].w;
dep[v] = dep[u]+1;
p[v][0] = u;
dfs(v);
}
}
}
void makep(int st){
dfs(st);
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
if(p[i][j-1]!=-1)
p[i][j] = p[p[i][j-1]][j-1];
}
int LCA(int a,int b){
if(dep[a] < dep[b])swap(a,b);
int i;
for(i = 0; (1<<i)<=dep[a];i++);
i--;
for(int j=i;j>=0;j--)
if(dep[a]-(1<<j)>=dep[b])
a = p[a][j];
if(a == b)return a;
for(int j = i;j >= 0;j--){
if(p[a][j] != -1 && p[a][j] != p[b][j]){
a = p[a][j];
b = p[b][j];
}
}
return p[a][0];
}
int main(){
int T;
cin>>T;
for(int cas = 1;cas <= T;cas++){
se.clear();
printf("Case #%d:\n",cas);
memset(head,-1,sizeof(head));
memset(p,-1,sizeof(p));
memset(dep,0,sizeof(dep));
memset(dir,0,sizeof(dir));
cnt = t = 0;
scanf("%d%d",&n,&q);
int u,v,w;
dep[1] = 1;
for(int i=1;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
addedge(u, v, w);
}
makep(1);
for(int i=0;i<t;i++)dfn[tme[i]] = i;
//for(int i=0;i<t;i++)printf("%d ",tme[i]);
int st = 0;
while(q--){
int op,num;
scanf("%d%d",&op,&num);
if(op == 1){
if(se.empty()){
puts("0");
se.insert(point(num));
continue;
}
if(se.find(point(num))!=se.end()){
printf("%d\n",st);
continue;
}
set<point>::iterator it = se.lower_bound(point(num));
if(it == se.begin() || it == se.end()){
int x = (se.begin())->id,y = (--se.end())->id;
st += (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
}
else{
int x = (it--)->id, y = (it)->id;
st += (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
}
se.insert(point(num);
printf("%d\n",st);
}else{
if(se.find(point(num))==se.end()){
printf("%d\n",st);
continue;
}
se.erase(point(num));
if(se.empty()){
puts("0");
se.insert(point(num));
continue;
}
set<point>::iterator it = se.lower_bound(point(num));
if(it == se.begin() || it == se.end()){
int x = (se.begin())->id,y = (--se.end())->id;
st -= (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
}
else{
int x = (it--)->id, y = (it)->id;
st -= (dir[num] - dir[LCA(x,num)]-dir[LCA(y,num)] + dir[LCA(x,y)]);
}
printf("%d\n",st);
}
}
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/zava_1087/article/details/47084035