## Prufer code
- [Oi wiki Prüfer code](https://oi-wiki.org/graph/prufer/#pr%C3%BCfer-%E5%BA%8F%E5%88%97%E7%9A%84%E6%80%A7%E8%B4%A8)
Prüfer 是這樣建立的:每次選擇一個編號最小的葉結點並刪掉它,然後在序列中記錄下它連接到的那個結點,重複 $n-2$ 次後就只剩下兩個結點,算法結束
??? note "範例圖"
### 性質
1. 在構造完 Prüfer 序列後原樹中會剩下兩個結點,其中一個一定是編號最大的點 n
2. 每個結點在序列中出現的次數是其度數減 1(沒有出現的就是葉結點)
下面是模板題
???+note "[CSES - Prüfer Code](https://cses.fi/problemset/task/1134)"
給定長度為 $n-2$ 的 Prüfer 序列,求此 Prüfer 序列構成的樹
$3 \le n \le 2 \cdot 10^5$
??? note "思路"
維護當前的 leaf 有哪些即可
??? note "code"
```cpp linenums="1"
#include
#define int long long
#define pii pair
#define pb push_back
#define mk make_pair
#define F first
#define S second
#define ALL(x) x.begin(), x.end()
using namespace std;
const int INF = 2e18;
const int MAXN = 3e5 + 5;
const int M = 1e9 + 7;
set st;
int n;
int a[MAXN];
int cnt[MAXN];
void init() {
cin >> n;
for (int i = 1; i <= n; i++) st.insert(i);
int x;
for (int i = 1; i <= n - 2; i++) {
cin >> a[i];
cnt[a[i]]++;
if (st.find(a[i]) != st.end()) st.erase(st.find(a[i]));
}
}
void solve() {
for (int i = 1; i <= n - 2; i++) {
int x = *st.begin();
st.erase(st.begin());
cout << x << " " << a[i] << '\n';
cnt[a[i]]--;
if (cnt[a[i]] == 0) st.insert(a[i]);
}
int x = *st.begin();
st.erase(st.begin());
int y = *st.begin();
cout << x << " " << y << '\n';
}
signed main() {
// ios::sync_with_stdio(0);
// cin.tie(0);
int t = 1;
//cin >> t;
while (t--) {
init();
solve();
}
}
```
???+note "[全國賽 2022 pG](https://sorahisa-rank.github.io/nhspc-fin/2022/problems.pdf#page=21)"
設 $T$ 為一棵有 $n$ 個節點的樹,節點編號 $1, 2, \ldots , n$,已知 $T$ 每個節點的 degree 為 $d_1,d_2,\ldots ,d_n$,其中 $d_i$ 為點 $i$ 的 degree,求出 $T$ 所有可能的 Prüfer 序列中,字典序第 $k$ 小的,如果沒有輸出 $-1$
$3
![Image title](./images/132.png){ width="300" }
$\log$ 的計算 : $-\log(a-1)! + \log a! - \log b! + \log (a-1)!$
$C^n_k\pmod{10^9+7}$ 的計算 : $\times b \times \text{inv}(a)$
??? note "code"
```cpp linenums="1"
#include
#define int long long
#define pii pair
#define pb push_back
#define mk make_pair
#define F first
#define S second
#define ALL(x) x.begin(), x.end()
using namespace std;
const double mxLog = 9;
const int INF = 1e18;
const int maxn = 3e5 + 5;
const int M = 1e9 + 7;
const long double EPS = 1e-8;
int n, k;
int d[maxn];
double preLog[maxn]; // preLog[i] = log(i!)
int prei[maxn], pinv[maxn], pref[maxn];
void build() {
preLog[0] = 0;
for (int i = 1; i <= n; i++) {
preLog[i] = preLog[i - 1] + log10(i);
}
prei[0] = prei[1] = pinv[0] = pinv[1] = pref[0] = pref[1] = 1;
for (int i = 2; i < maxn; i++) {
pref[i] = pref[i - 1] * i % M;
pinv[i] = (M - (M / i) * pinv[M % i] % M) % M;
prei[i] = prei[i - 1] * pinv[i] % M;
}
}
vector work(int _n, int _k, const int _d[]) {
n = _n;
k = _k;
k--;
for (int i = 1; i <= n; i++) {
d[i] = _d[i];
d[i]--;
}
build();
vector ans;
for (int t = n - 2; t >= 1; t--) {
int f, flag = false;
for (int i = 1; i <= n; i++) {
if (d[i]) {
f = i;
break;
}
}
double big = preLog[t - 1];
int small = pref[t - 1];
for (int i = 1; i <= n; i++) {
if (i == f) {
big = big - preLog[d[i] - 1];
small = (small * prei[d[i] - 1]) % M;
} else if (d[i]) {
big = big - preLog[d[i]];
small = (small * prei[d[i]]) % M;
}
}
int val;
if (big - mxLog > EPS) {
val = INF;
} else {
val = small;
}
for (int i = 1; i <= n; i++) {
if (d[i]) {
if (i != f) {
big += preLog[d[f] - 1] + preLog[d[i]];
big -= preLog[d[f]] + preLog[d[i] - 1];
small = (((small * pinv[d[f]]) % M) * d[i]) % M;
if (big - mxLog > EPS) {
val = INF;
} else {
val = small;
}
f = i;
}
if (k >= val) {
k -= val;
} else {
ans.pb(i);
d[i]--;
flag = true;
break;
}
}
}
if (flag == false) {
return {-1};
}
}
return ans;
}
signed main() {
int n, k;
cin >> n >> k;
int d[1005];
for (int i = 1; i <= n; i++) cin >> d[i];
vector ans = work(n, k, d);
for (auto ele : ans) cout << ele << '\n';
}
```