砍樹枝 衝刺noip

砍樹枝
代碼測試區
題目:
這天,CD 作爲moreD 的寵物,又被殘酷地訓練爬樹了,moreD 保證了這棵樹滿足從任意一個點出發,CD 都能走到所有的點,CD 每天都要爬過所有的點才能回家吃飯。經過一天又一天殘酷的訓練以後,CD 已經忍無可忍了,於是CD 會憤怒地誤傷一條樹枝,一條樹枝被誤傷以後就不可以再走了。當然,CD 不符合寵物法則的行動怎麼會逃過moreD 的眼睛,moreD 決定,每當CD 誤傷一條樹枝,他都會再重新加一條樹枝,可是他不知道加完以後,這隻寵物是否還能從任意一個點出發到達所有的點,要是不能,豈不是讓這隻寵物得逞了麼?問題是,現在moreD 也不知道CD 最終會誤傷哪一條樹枝,於是現在moreD臆測出了許多種可能,你要告訴moreD:如果CD 誤傷了第z 條邊,他再在編號爲x 的點和編號爲y 的點之間加一條邊,CD 是否能得逞。
輸入格式:
第一行一個正整數n,表示節點個數。接下來n-1 行,每行兩個正整數x,y,表示原來樹上存在一條連接編號爲x的節點和編號爲y 的節點的邊。第n+1 行一個正整數Q,表示詢問次數。接下來Q 行,每行三個正整數x,y,z,表示一個詢問(含義如題所示)。
輸出格式:
輸出共Q 行。對於每一個詢問,如果CD 會得逞就輸出YES,否則輸出NO。
樣例輸入1::
5
1 2
2 3
2 4
4 5
3
2 5 3
2 3 1
1 5 2
樣例輸出1:
NO
YES
YES
數據範圍:
對於20%的數據保證n,Q≤1000。對於另外20%的數據保證n,Q≤10000 且樹爲隨機生成。對於70%的數據保證n,Q≤200000。對於100%的數據保證n≤200000,Q≤2000000。

題解:
知識點: dfs序
講解:
這一題太坑了,一開始看是lca結果gg了,後面想了想處理點的重合纔是lca(有興趣瞭解的點這),邊就不是了,邊應該用dfs序。
分析一下題目,如果CD破壞了一條邊的話,那麼整個樹就變成了兩個聯通塊,如果moreD加的一條邊可以連通兩個聯通塊的話那麼CD就不能得逞,如果不能連通的話就可以得逞。現在問題就是如何判斷一條邊能否連通兩個聯通塊,很明顯如果邊的兩點分別在兩個聯通塊的話那就肯定可以聯通了,現在我們就要判斷兩個點是否分別在兩個聯通塊。再看一下這兩個連通塊的特點,我們可以確定這兩個聯通塊一定是兩棵樹,而且一棵還是一棵子樹,那麼很容易想到dfs序,因爲在dfs序同一棵樹中的dfs序是連續的,這樣我們只要找到分割後的子樹的dfs序,看一下兩個點的dfs序是否一個在裏面另一個不在。
因爲這一題的原因我們不知道所給的邊哪個是父親哪個是兒子,這樣哪個是子樹也不好確定(因爲兒子在的聯通塊一定是子樹,好好想想),不過我們知道兒子的dfs序範圍一定比父親小,所以我們只要選in中最大的,out中的最小的就好了。
上代碼:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int in[200100],out[200100],head[400100],Next[400100],ver[400100],size=0,n,x[200100],y[200100],vist[200100];
void add(int x,int y)
{
	size++;
	Next[size]=head[x];
	head[x]=size;
	ver[size]=y;
	return ;
}
void dfs(int x)
{
	size++;//dfs序常規++ 
	in[x]=size;//統計一下這一個點開始的dfs序 
	for(int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if(vist[y]==1)
			continue;
		vist[y]=1;
		dfs(y);
	}
	out[x]=size;//統計一下結束的dfs序,這樣從開始到結束就是以它爲根的子樹的各個點的dfs序 
}
int main()
{
	int m;
	scanf("%d",&n);
	for(int i=1;i<n;i++)//讀入樹 
	{
		scanf("%d %d",&x[i],&y[i]);
		add(x[i],y[i]);
		add(y[i],x[i]);
	}
	size=0;//初始化一下size 
	dfs(1);//dfs一下確定dfs序 
	scanf("%d",&m);//開始詢問 
	for(int i=1;i<=m;i++)
	{
		int x1,y1,z,l,r;
		scanf("%d %d %d",&x1,&y1,&z);//輸入數據 
		l=max(in[y[z]],in[x[z]]);//確定子樹 
		r=min(out[y[z]],out[x[z]]);//確定子樹 
		if(!(l<=in[x1] && out[x1]<=r) && l<=in[y1] && out[y1]<=r)//點x1在,y1不在 
			printf("NO\n");
		else if(!(l<=in[y1] && out[y1]<=r) && l<=in[x1] && out[x1]<=r)//點y1在,x1不在 
				printf("NO\n");
			else printf("YES\n");//剩下就是YES了 
	}
	return 0;
}

希望大家都可以AC。noip我來了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章