hdu 5296 lca+dfs應用,lca倍增法模板

題目大意:給出一棵樹,每個邊都有一個權值,現在有一個空的集合,兩種操作,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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章