HDU2586-How far away ?(離線Tarjan + 倍增)
Problem Description
There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.
Input
First line is a single integer T(T<=10), indicating the number of test cases.
For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
Output
For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
Sample Output
10
25
100
100
題意:求兩個節點之間的最短距離。
樹剖可以解決此題,但是程式碼量比較大,就沒有寫。
如果用LCA解決此問題,那麼兩點之間的距離=dis[x]+dis[y]-2*dis[lca]
dis[i]代表 i 到根節點的距離。(此題根節點為1)
lca代表x和y的最近公共祖先。
先用離線Tarjan做了一下。
最近在寫倍增的程式碼,為了熟練運用,又寫了兩份倍增的程式碼。
第一個程式碼dis[i]陣列的含義是到根節點的距離。
第二個程式碼dis[x] [i] 代表x到 (x向上2^i個節點)的距離,可以在預處理倍增表的時候處理掉。
①Tarjan離線
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define eps 1e-8
#define memset(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long int LL;
const int MAXL(4*1e4);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
struct node
{
int to;
int next,w;
} edge[2*MAXL+50];
int head[2*MAXL+50];
int father[MAXL+50];
int ans[MAXL+50];
int dis[MAXL+50];
bool vis[MAXL+50];
int x[MAXL+50],y[MAXL+50];
struct nodee
{
int ed,id;
} nod;
vector<nodee>v[MAXL+50];
int cnt;
int Find(int x)
{
if(x!=father[x])
father[x]=Find(father[x]);
return father[x];
}
void Join(int x,int y)
{
int fx=Find(x),fy=Find(y);
if(fx!=fy)
father[fy]=fx;
}
void LCA(int u)
{
vis[u]=true;
for(int i=head[u]; ~i; i=edge[i].next)
{
int v=edge[i].to,w=edge[i].w;
if(!vis[v])
{
dis[v]=dis[u]+w;
LCA(v);
Join(u,v);
}
}
for(int i=0; i<v[u].size(); i++)
{
int ed=v[u][i].ed,id=v[u][i].id;
if(vis[ed]==true)
{
int lca=Find(ed);
ans[id]=lca;
}
}
}
void add_edge(int x,int y,int w)
{
edge[cnt].to=y;
edge[cnt].next=head[x];
edge[cnt].w=w;
head[x]=cnt++;
}
int n,q;
void init()
{
scanf("%d%d",&n,&q);
cnt=0;
memset(head,-1);
memset(vis,false);
memset(ans,0);
memset(dis,0);
for(int i=0; i<=n; i++)
v[i].clear();
for(int i=0; i<=n; i++)
father[i]=i;
for(int i=1; i<n; i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
add_edge(x,y,w);
add_edge(y,x,w);
}
for(int i=1; i<=q; i++)
{
scanf("%d%d",x+i,y+i);
nod.ed=y[i],nod.id=i;
v[x[i]].push_back(nod);
nod.ed=x[i],nod.id=i;
v[y[i]].push_back(nod);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
dis[1]=0;
LCA(1);
for(int i=1; i<=q; i++)
cout<<dis[x[i]]+dis[y[i]]-2*dis[ans[i]]<<endl;
}
}
②倍增1
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define eps 1e-8
#define swap(a,b) (a=a+b,b=a-b,a=a-b)
#define memset(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long int LL;
const int MAXL(4*1e4);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
struct node{
int to,next,w;
node(){}
node(int to,int next,int w):to(to),next(next),w(w){}
}edge[2*MAXL+50];
int head[2*MAXL+50];
int depth[MAXL+50];
int dp[MAXL+50][20];
int dis[MAXL+50];
bool vis[MAXL+50];
int n,q,cnt;
void init(){
cnt=0;
memset(head,-1);
memset(dp,0);
memset(depth,0);
memset(dis,0);
memset(vis,false);
}
void add_edge(int x,int y,int z){
edge[cnt]=node(y,head[x],z);
head[x]=cnt++;
}
void getDepth(int u){
vis[u]=true;
for(int i=head[u];~i;i=edge[i].next){
int to=edge[i].to,w=edge[i].w;
if(!vis[to]){
dis[to]=dis[u]+w;
depth[to]=depth[u]+1;
dp[to][0]=u;
getDepth(to);
}
}
}
void getDp(){
for(int up=1;(1<<up)<=n;up++)
for(int i=1;i<=n;i++)
dp[i][up]=dp[dp[i][up-1]][up-1];
}
int LCA(int x,int y){
if(depth[x]<depth[y])
swap(x,y);
int i=-1;
while((1<<(i+1))<=depth[x])
i++;
for(int j=i;j>=0;j--)
if(depth[x]-(1<<j)>=depth[y])
x=dp[x][j];
if(x==y) return x;
for(int j=i;j>=0;j--){
if(dp[x][j]!=dp[y][j]){
x=dp[x][j];
y=dp[y][j];
}
}
return dp[x][0];
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);
add_edge(y,x,z);
}
depth[1]=1;
getDepth(1);
getDp();
while(q--){
int x,y;
scanf("%d%d",&x,&y);
int lca=LCA(x,y);
cout<<dis[x]+dis[y]-2*dis[lca]<<endl;
}
}
}
②倍增2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define eps 1e-8
#define swap(a,b) (a=a+b,b=a-b,a=a-b)
#define memset(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long int LL;
const int MAXL(4*1e4);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
struct node{
int to,next,w;
node(){}
node(int to,int next,int w):to(to),next(next),w(w){}
}edge[2*MAXL+50];
int head[2*MAXL+50];
int depth[MAXL+50];
int dp[MAXL+50][20];
int dis[MAXL+50][20];
bool vis[MAXL+50];
int n,q,cnt;
void init(){
cnt=0;
memset(head,-1);
memset(dp,0);
memset(depth,0);
memset(dis,0);
memset(vis,false);
}
void add_edge(int x,int y,int z){
edge[cnt]=node(y,head[x],z);
head[x]=cnt++;
}
void getDepth(int u){
vis[u]=true;
for(int i=head[u];~i;i=edge[i].next){
int to=edge[i].to,w=edge[i].w;
if(!vis[to]){
depth[to]=depth[u]+1;
dis[to][0]=w;
dp[to][0]=u;
getDepth(to);
}
}
}
void getDp(){
for(int up=1;(1<<up)<=n;up++){
for(int i=1;i<=n;i++){
dp[i][up]=dp[dp[i][up-1]][up-1];
dis[i][up]=dis[i][up-1]+dis[dp[i][up-1]][up-1];
}
}
}
int LCA(int x,int y){
int ans=0;
if(depth[x]<depth[y])
swap(x,y);
int i=-1;
while((1<<(i+1))<=depth[x])
i++;
for(int j=i;j>=0;j--)
if(depth[x]-(1<<j)>=depth[y])
ans+=dis[x][j],x=dp[x][j];
if(x==y) return ans;
for(int j=i;j>=0;j--){
if(dp[x][j]!=dp[y][j]){
ans+=dis[x][j];
ans+=dis[y][j];
x=dp[x][j];
y=dp[y][j];
}
}
return ans+dis[x][0]+dis[y][0];
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);
add_edge(y,x,z);
}
depth[1]=1;
getDepth(1);
getDp();
while(q--){
int x,y;
scanf("%d%d",&x,&y);
int ans=LCA(x,y);
cout<<ans<<endl;
}
}
}