[CSP-S模擬測試]:木葉下(圖論)

題目傳送門(內部題77)


輸入格式

  第一行一個整數$n$,表示原先的樹的點數接下來$n-1$行每行兩個整數$a,b$,表示原先的樹上的$n-1$條邊,保證這$n-1$條邊造成一棵樹。
  接下來一行一個整數$m$,表示不一樣的方案數。
  接下來$m$行每行兩個整數$u,v$,表示$m$個不一樣的加邊方案,每行的方案表示在那一行給出的$u,v$之間加一條邊。注意若是$u=v$,說明加了一條自環致使加邊失敗,此時最後留下$0$個點。
html


輸出格式

  $m$行,第$i$行一個整數表示第$i$次詢問的答案。c++


樣例

見下發文件算法


數據範圍與提示

  全部的測試點,$n\leqslant 200,000,m\leqslant 200,000$
  第$1$個測試點,保證一開始編號相差爲$1$的任意兩個點之間有一條邊,也就是說給出的樹形態是一條鏈。
  第$2,3,4$個測試點,全部的加邊方案知足$u=v$。第$2$個測試點還知足$n\leqslant 500$
  第$5,6,7$個測試點,$n\leqslant 500,m\leqslant 500$
  第$8$個測試點,全部的加邊方案知足$u,v$在原樹上相鄰,也就是有邊直接相連。
  第$9$個測試點,全部的加邊方案知足$u,v$在原樹上距離爲$2$,也就是說,$u!=v$,且存在點$z$,既和$u$相鄰也和$v$相鄰。
  第$10,11,12$個測試點,樹是隨機生成的,隨機方式的僞代碼爲:
  $for\ i\ in\ [2...n]$
    $addedge(i,rand()\%(i-1)+1)$
  也就是說,對$2$到$n$這$n-1$個點,分別隨機選擇一個編號更小的點與其連邊。
  第$13,14,15$個測試點,全部的加邊方案知足$u=1$
  第$10,13,16,17$個測試點,不會出現$u=v$的狀況。
  第$18$到$20$個測試點,無特殊性質。
測試


題解

能夠發如今加邊失敗(加入自環)的狀況下,答案就是樹的直徑的一半(上取整)。優化

加邊以後無非就是縮掉那個環後新的數的直徑的一半(上取整),可是顯然時間不容許咱們這麼作,因而考慮怎麼快速求出加邊以後的貢獻。spa

不妨把環上的點當作黑點,其它的點當作白點,設$dis[i]$表示從$i$到最近黑點的距離,那麼答案即爲$\max(dis[i])$。htm

那麼則須要算出來對於每個黑點,其控制了哪些白點。blog

不妨設$g[i]$表示從$i$出發向其子樹內走,能走的最深的距離。get

對於每一個點,預處理出來其兒子中最大的三個$g[i]$,並記錄分別是誰。it

這樣就能在$\Theta(1)$的時間複雜度內計算出不容許第一步走向黑點的狀況下能一直向下走的最遠距離。

可是顯然咱們不能直接計算全部黑點。

利用樹上倍增優化算法。

不妨設$q[i]$表示從$i$的父親走向其子樹而不通過$i$所可以到達的最遠距離,預處理便可。

而後利用倍增對$q[i]$進行樹上路徑取$\max$便可。

時間複雜度:$\Theta(n\log n)$。

指望得分:$100$分。

實際得分:$100$分。


代碼時刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to;}e[400001];
int head[200001],cnt;
int n,m;
pair<int,int> mx[200001][4];
int mxdep;
int w[200001];
int depth[200001],fa[200001][21],maxn[200001][21];
void add(int x,int y)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
int dfs1(int x,int fa)
{
	depth[x]=depth[fa]+1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		if(e[i].to==fa)continue;
		int flag=dfs1(e[i].to,x);
		if(flag>mx[x][3].second)mx[x][3]=make_pair(e[i].to,flag);
		if(mx[x][3].second>mx[x][2].second)swap(mx[x][3],mx[x][2]);
		if(mx[x][2].second>mx[x][1].second)swap(mx[x][2],mx[x][1]);
	}
	mxdep=max(mxdep,mx[x][1].second+mx[x][2].second+1);
	return mx[x][1].second+1;
}
int find1(int x,int y)
{
	if(x==y||!y)return 0;
	for(int i=1;i<=3;i++)if(mx[x][i].first&&mx[x][i].first!=y)return mx[x][i].second;
	return 0;
}
int find2(int x,int _,int __)
{
	if(x==_||x==__||!_||!__)return 0;
	for(int i=1;i<=3;i++)if(mx[x][i].first&&mx[x][i].first!=_&&mx[x][i].first!=__)return mx[x][i].second;
	return 0;
}
void dfs2(int x,int fat)
{
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fat)
		{
			maxn[e[i].to][0]=find1(x,e[i].to);
			fa[e[i].to][0]=x;
			w[e[i].to]=max(w[x]+1,maxn[e[i].to][0]+1);
			for(int j=1;j<=20;j++)
			{
				fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
				maxn[e[i].to][j]=max(maxn[e[i].to][j-1],maxn[fa[e[i].to][j-1]][j-1]);
			}
			dfs2(e[i].to,x);
		}
}
int LCA(int x,int y)
{
	if(depth[x]>depth[y])swap(x,y);
	int res=mx[y][1].second;
	for(int i=20;i>=0;i--)
		if(depth[fa[y][i]]>=depth[x])
		{
			res=max(res,maxn[y][i]);
			y=fa[y][i];
		}
	if(x==y)return max(res,w[x]);
	res=max(res,mx[x][1].second);
	for(int i=20;i>=0;i--)
		if(fa[x][i]!=fa[y][i])
		{
			res=max(res,maxn[y][i]);
			res=max(res,maxn[x][i]);
			x=fa[x][i]; 
			y=fa[y][i];
		}
	res=max(res,find2(fa[x][0],x,y));
	res=max(res,w[fa[x][0]]);
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	dfs1(1,0);
	dfs2(1,0);
	scanf("%d",&m);
	while(m--)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		if(u==v)printf("%d\n",(mxdep+1)/2);
		else printf("%d\n",LCA(u,v));
	}
	return 0;
}

rp++

相關文章
相關標籤/搜索