代碼:
//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;
}
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序對應的點。套上面的公式即可。
不明白怎麼回事的自己畫棵樹比劃比劃就行吧。
自己寫的模板太爛了,直接爆棧了,網上找了一模板自己又寫了一邊,屯一下。思路模板都不是我的,真是弱爆了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.