1. 程式人生 > >NOIP模擬 相遇(LCA+樹狀陣列+dfs序)

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;
}