1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——CH#56C(樹上差分+set維護)

藍書(演算法競賽進階指南)刷題記錄——CH#56C(樹上差分+set維護)

題目:CH#56C.

題目大意:給定一棵樹,維護一個點集,支援:

1.給點集加一個樹點.

2.給點集刪一個樹點.

3.詢問將點集中所有樹點連線起來的連通塊的最小邊集總長度.

這道題看起來毫無思緒,但是仔細想就可以得出一些性質.

我們考慮將點集中所有點按照dfs序排序並連成一個環,很顯然最小邊集總長就是點集形成的環中相鄰兩點之間的距離之和除以2.

那麼這道題就十分容易了,我們維護一個支援插入刪除和查詢前驅後繼的資料結構(例如set),就可以在每一次插入一個點時,將這個點的前驅後繼之間的距離減去,加上前驅到這個點以及這個點到後繼的距離即可,刪除類似.時間複雜度O((n+m)logn).

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
int n,m;
struct side{
  int y,next,v;
}e[N*2+9];
int lin[N+9],top,ord[N+9],num;
LL ans;
set<int>s;
struct node{
  int son,fa,deep,top,size,dfn;
  LL v;
}d[N+9];
int ri(){
  int x=0;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar());
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x;
}
char rc(){
  char c=getchar();
  while (c^'+'&&c^'-'&&c^'?') c=getchar();
  return c;
}
void ins(int x,int y,int v){e[++top].y=y;e[top].v=v;e[top].next=lin[x];lin[x]=top;}
int L(int x){return s.find(x)==s.begin()?*--s.end():*--s.find(x);}
int R(int x){return s.find(x)==--s.end()?*s.begin():*++s.find(x);}
void dfs1(int k,int fa){
  d[k].fa=fa;
  d[k].deep=d[fa].deep+1;
  d[k].size=1;
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^fa){
      d[e[i].y].v=d[k].v+1LL*e[i].v;
      dfs1(e[i].y,k);
      d[k].size+=d[e[i].y].size;
      if (d[d[k].son].size<d[e[i].y].size) d[k].son=e[i].y;
    }
}
void dfs2(int k,int start){
  d[k].top=start;
  d[k].dfn=++num;
  ord[num]=k;
  if (d[k].son) dfs2(d[k].son,start);
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^d[k].son&&e[i].y^d[k].fa) dfs2(e[i].y,e[i].y);
}
int LCA(int x,int y){
  while (d[x].top^d[y].top)
    d[d[x].top].deep>d[d[y].top].deep?x=d[d[x].top].fa:y=d[d[y].top].fa;
  return d[x].deep<d[y].deep?x:y;
}
Abigail into(){
  n=ri();
  int x,y,v;
  for (int i=1;i<n;i++){
    x=ri();y=ri();v=ri();
    ins(x,y,v);ins(y,x,v);
  }
}
Abigail work(){
  dfs1(1,0);
  dfs2(1,1);
}
#define distance(x,y) d[ord[x]].v+d[ord[y]].v-2*d[LCA(ord[x],ord[y])].v
Abigail getans(){
  char opt;
  int x,l,r;
  m=ri();
  for (int i=1;i<=m;i++){
    opt=rc();
    switch (opt){
      case '+':x=d[ri()].dfn;
               if (s.count(x)) continue;
               s.insert(x);
               l=L(x);r=R(x);
               ans=ans-distance(l,r)+distance(l,x)+distance(x,r);
               break;
      case '-':x=d[ri()].dfn;
               if (!s.count(x)) continue;
               l=L(x);r=R(x);
               ans=ans-distance(l,x)-distance(x,r)+distance(l,r);
               s.erase(x);
               break;
      case '?':printf("%lld\n",ans>>1LL);
               break;
    }
  }
}
int main(){
  into();
  work();
  getans();
  return 0;
}