1. 程式人生 > 實用技巧 >prufer 序列 學習筆記

prufer 序列 學習筆記

前言

上週五看了一下,發現不是很難,今天再看了一眼,把板題做了,順便看了另外一道(懶得碼了)

正文

其實很簡單,我們定義一顆無根樹的 prufer 序列為,欽定任意一個點為根(方便確定父子關係),每次從葉子中選出一個編號最小的點,把它的父親加入到 prufer 序列中,並刪掉該節點。

不難看出,我們最後會有 \(n-2\) 個數,而且可以 \(n\log n\) 求出。並且不同樹的 prufer 序列不會相同。實際上我們也可以做到 \(\Theta(n)\) 。大概意思就是指標維護當前編號最小的葉子,然後每次刪點的時候把會立馬加入到序列的父親也跟著刪掉就好了,具體見程式碼。

考慮如何從 prufer 序列轉換成一棵樹。不難看出,一個點的度數就是 prufer 序列中的出現次數。我們可以先把葉子提出來,每次對於 prufer 序列中的一個點,葉子節點中編號最小的就是它的兒子。然後刪掉這個兒子,看是否是葉子即可。時間複雜度 \(\Theta(n\log n)\)

。不過我們也可以做到 \(\Theta(n)\),與上面類似。

不難發現的是,對於任意一個 prufer 序列,我們都可以得到相應的一棵樹。於是,\(n\) 個點的生成樹個數就是 \(n^{n-2}\) 個。相應的,我們可以推出每個點度數固定時的生成樹個數。

\(\texttt{Code}\)

板題傳送門

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define ll long long
#define MAXN 5000005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

ll ans;
int n,m,f[MAXN],seq[MAXN],deg[MAXN];

signed main(){
	read (n,m);
	if (m == 1){
		for (Int i = 1;i < n;++ i) read (f[i]),deg[f[i]] ++;int now = 0;
		for (Int i = 1,j = 1;i <= n - 2;++ i,++ j){
			while (deg[j]) ++ j;seq[i] = f[j];
			while (i <= n - 2 && !-- deg[seq[i]] && seq[i] < j) seq[i + 1] = f[seq[i]],++ i;
		}
		for (Int i = 1;i <= n - 2;++ i) ans ^= 1ll * i * seq[i];
	}
	else{
		for (Int i = 1;i < n - 1;++ i) read (seq[i]),deg[seq[i]] ++;seq[n - 1] = n;
		for (Int i = 1,j = 1;i < n;++ i,++ j){
			while (deg[j]) j ++;f[j] = seq[i];
			while (i < n && !-- deg[seq[i]] && seq[i] < j) f[seq[i]] = seq[i + 1],++ i;
		}
		for (Int i = 1;i <= n - 1;++ i) ans ^= 1ll * i * f[i];
	}
	write (ans),putchar ('\n');
	return 0;
}