NOIP模擬 相遇(LCA+樹狀陣列+dfs序)
【題目描述】
豪哥生活在一個n個點的樹形城市裡面,每一天都要走來走去。雖然走的是比較的多,但是豪哥在這個城市裡面的朋友並不是很多。
當某一天,猴哥給他展現了一下大佬風範之後,豪哥決定要獲得一些交往機會來提升交往能力。豪哥現在已經物色上了一條友,打算和它(豪哥並不讓吃瓜群眾知道性別)交往。豪哥現在spy了一下這個人的所有行程起點和終點,豪哥打算從終點開始走到起點與其相遇。但是豪哥是想找話題的,他想知道以前有多少次行程和此次行程是有交集的,這樣豪哥就可以搭上話了。這個路徑與之前路徑的有交集數量作為豪哥此次的交往機會。
但是豪哥急著要做交往準備,所以算什麼交往機會的小事情就交給你了。
【輸入格式】
第一行一個正整數n表示節點個數。接下來n-1行,每行兩個正整數分別是u,v表示節點u和v之間有連邊。接下來一行一個 正整數m表示路徑個數。然後有m行,每行兩個正整數分別是u,v分別表示u到v之間有一條路徑。
【輸出格式】
輸出共m行,每行一個整數,第i行表示豪哥在這條路徑上獲得的交往機會。
【樣例輸入】
5
1 2
1 3
3 4
3 5
4
4 5
4 2
1 3
1 2
【樣例輸出】
0
1
2
2
【備註】
對於20%的資料n,m≤2000
對於另外20%的資料n,m≤50000
對於另外10%的資料n,m≤200000保證樹形結構是一條鏈
對於另外50%的資料n,m≤200000
【題目分析】
(口胡開始)
我們先找一下對於兩條路徑(a,b)和(c,d),如果他們有交集有什麼規律?
不難發現,如果兩條路徑有交集,那麼對於lca(a,b)和lca(c,d),要麼lca(a,b)在路徑(c,d)上,要麼lca(c,d)在(a,b)上,要麼兩個lca相同,證明就一句話,感性理解:假設我們現在手上是一條 lca 深度大的路徑,這條路徑最高可達點就是 lca,若 lca不在另一條路徑上,又因為他lca深度大,所以不可能在另外一條路徑 lca上方,所以就沒有可達點了。
所以這個問題就轉化為兩個問題:1.當前路徑上有幾個之前的lca;2.之前路徑有幾個經過當前路徑的lca;(當然要特判一下lca相同的時候)
1.當前路徑上找lca
因為這是一顆靜態樹,所以考慮用dfs序來解決。對於一個點x,a->lca(a,b)這樣的樹鏈如果經過他,則一定是從它的子樹走向它的祖先。 然後就比較顯然了,每多加一個這樣的x,則給他子樹中所有點的權值都+1. 查詢(a,lca(a,b),b)的時候,就用 a權+b權-lca權*2. 若 a與lca在x的上下兩方,則會被累加到貢獻。若同時在x的下方,則會被 lca權減掉。
2.在之前路徑上找lca
同樣地考慮dfs序, 很方便的樹上字首和。若有一條路徑(a,b),則給lca(a,b) +1,a-1,b-1.然後查詢一個點到根的權值和,這是離線時的套路。 但這題這樣不好做,因為不能每一次都重構樹,於是我們變一下,變成類似樹上字尾和的東西。
對於一條路徑(a,b),給a+1,b+1,lca-2. 一個點的子樹權值和就是他的權值。
用兩樹狀陣列維護一下dfs 序上的資訊,我們就把這道題給搞定了。
這題的坑點主要在於,兩個樹狀陣列維護的資訊意義是不一樣的,壓根不可以相提並論。
【程式碼~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
#define lowbit(x) ((x)&(-x))
#define treesum(t,x) (sum(t,r[x])-sum(t,l[x]-1))
int n,m,stm,cnt;
int head[MAXN],depth[MAXN];
int l[MAXN],r[MAXN];
int to[MAXN<<1],nxt[MAXN<<1];
int f[MAXN][19];
int t1[MAXN],t2[MAXN],ans;
int app[MAXN];
void change(int *tr,int x,int v)
{
for(;x<=n;x+=lowbit(x))
tr[x]+=v;
}
int sum(int *tr,int x)
{
int ret=0;
for(;x;x-=lowbit(x))
ret+=tr[x];
return ret;
}
void add(int x,int y)
{
cnt++;
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
}
void dfs(int u,int fa)
{
l[u]=++stm;
f[u][0]=fa;
for(int i=1;i<19;++i)
f[u][i]=f[f[u][i-1]][i-1];
depth[u]=depth[fa]+1;
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(v!=fa)
{
dfs(v,u);
}
}
r[u]=stm;
}
int lca(int x,int y)
{
if (depth[x]<depth[y])
swap(x,y);
for(int i=18;i>=0;i--)
if(depth[f[x][i]]>=depth[y])
x=f[x][i];
if(x==y)
return x;
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void calcans(int x,int y,int lc)
{
ans+=treesum(t1,lc);
ans+=sum(t2,l[x])+sum(t2,l[y])-sum(t2,l[lc])*2;
}
int main()
{
memset(head,-1,sizeof(head));
memset(nxt,-1,sizeof(nxt));
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,-1);
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
int lc=lca(x,y);
ans=0;
calcans(x,y,lc);
printf("%d\n",ans+app[lc]);
app[lc]++;
change(t1,l[x],1);
change(t1,l[y],1);
change(t1,l[lc],-2);
change(t2,l[lc],1);
change(t2,r[lc]+1,-1);
}
return 0;
}