1. 程式人生 > >[AGC005C Tree Restoring] [貪心構造]

[AGC005C Tree Restoring] [貪心構造]

[題目大意]

青木君很喜歡數列和樹。

一天,高橋君給了他一個長度為N的數列a1, a2, …, aN,這讓青木有了構造一棵樹的衝動。

他想要構造一棵樹,其中i號點與其它點的樹上距離的最大值恰好等於ai(假設樹邊長度均為1)。

請問是否存在這樣一棵樹符合要求。

[想法]

這種題目可以從極端情況入手,而這裡的極端就是樹的直徑。

(1)因為任何樹都存在至少一條直徑,所以一定存在一對相等且最大的a[u]和a[v],其中u和v是直徑的兩端,如果不存在,輸出Impossible;

(2)找到直徑後,直徑上的所有點的a[i]都可以推出,如果給出的a序列中找不到對應的值,輸出Impossible;

(3)接下來,其餘點都相當於掛在直徑這條鏈的兩側(不允許掛在u和v上,否則直徑會更長)。

         (3-1)顯然,如果這些點的a[i]值沒有大於直徑的一半(向上取整)mid,它們不可能掛在鏈的外側,所以輸出Impossible;

         (3-2)否則,一定可以構造出符合條件的懸掛方式:由於剩餘點的a[i]∈[mid+1,a[u]],所以一定可以在直徑上找到一個距離u恰好為a[i]-1的點x,將i和x連線,使得a[i]符合要求。同時,所有點都是直接懸掛的,所以不會產生更長的鏈,可以發現其它點的a[i]仍然符合要求。

注意一個小坑點:a={1,1},這種情況顯然應該輸出Possible,但是程式考慮不周的話可能會出現小BUG,如果莫名WA,可以試試這個樣例。

#include <cstdio>
#include <algorithm>
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=1e5+5;
int n,i,d,a[N],cnt[N];
int main()
{
	scanf("%d",&n);
	rep(i,1,n) {
		scanf("%d",&a[i]);
		cnt[a[i]]++;
	}
	sort(a+1,a+1+n);
	if (a[n-1]!=a[n]) {
		printf("Impossible\n");
		return 0;
	}
	cnt[a[n]]-=2;
	rep(i,1,a[n]-1)
	{
		d=max(i,a[n]-i);
		if (cnt[d]<1) {
			printf("Impossible\n");
			return 0;
		}
		cnt[d]--;
	}
	rep(i,1,(a[n]+1)/2)
		if (cnt[i]) {
			printf("Impossible\n");
			return 0;
		}
	printf("Possible\n");
	return 0;
}