Purfer序列
Posted Kalzn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Purfer序列相关的知识,希望对你有一定的参考价值。
Purfer序列是一个元素值域均为[1,n]的,n-2个整数构成的序列。其可以与一个n阶完全图的生成树一一对应,形成双射。凯莱定理即由此而来。
构造方法,
树构造purfer
每次选择编号最小的叶子节点,删去,然后把它的父节点的编号加入到序列后面,最后只剩下两个节点时算法停止。
从上述构造 Prufer 序列的过程可以看出 Prufer 序列具有以下两个性质:
1、在构造完 Prufer 序列后原树中会剩下两个节点,其中一个一定是编号最大的点 n。
2、每个节点在序列中出现的次数是其度数减 1,因此没有出现的就是叶节点。
purfer构造树
每次我们选择一个编号最小的度数为 1 的节点,与当前枚举到的 Prufer 序列的点连接,然后同时减掉两个点的度数。重复 n-2 次后就只剩下两个度数为 1 的节点,其中一个是 n,把它们连接起来即可。
P6086 【模板】Prufer 序列
下面时模板代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <bitset>
#include <array>
#include <cctype>
#include <time.h>
#pragma GCC optimize(2)
void read_f() freopen("1.in", "r", stdin); freopen("1.out", "w", stdout);
void fast_cin() std::ios::sync_with_stdio(false); std::cin.tie();
void run_time() std::cout << "ESC in : " << clock() * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl;
template <typename T>
bool bacmp (const T & a, const T & b) return a > b;
template <typename T>
bool pecmp (const T & a, const T & b) return a < b;
#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr(x, y) (make_pair((x), (y)))
#define pb(x) push_back(x);
using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const int N = 5e6 + 7;
int n, f[N], p[N], d[N];
ll ans;
inline void TP()
for (int i = 1; i < n; i++) ++d[f[i]];
for (int i = 1, j = 1; i <= n - 2; i++, j++)
while (d[j]) ++j; p[i] = f[j];
while (i <= n - 2 && !--d[p[i]] && p[i] < j) p[i+1] = f[p[i]], ++i;
for (int i = 1; i <= n - 2; i++) ans ^= 1ll * i * p[i];
inline void PT()
for (int i = 1; i <= n - 2; i++) ++d[p[i]];
p[n-1] = n;
for (int i = 1, j = 1; i < n; i++, j++)
while (d[j]) ++j; f[j] = p[i];
while (i < n && !--d[p[i]] && p[i] < j) f[p[i]] = p[i+1], ++i;
for (int i = 1; i < n; i++) ans ^= 1ll * i * f[i];
int main()
int m;
scanf("%d%d", &n, &m);
if (m == 1)
for (int i = 1; i < n; i++) scanf("%d", &f[i]);
TP();
else
for (int i = 1; i <= n-2; i++) scanf("%d", &p[i]);
PT();
printf("%lld\\n", ans);
return 0;
purfer序列主要应用与生成树的计数。它有如下3种性质:
1、purfer序列与带编号无根树形成双射。
2、度数为di的点会在prufer中出现di-1次。
3、对于给定度数d1~n的一颗无根树,共有
(
n
−
2
)
!
∏
i
−
1
n
(
d
i
−
1
)
!
\\frac(n-2)!\\prod_i-1^n(d_i-1)!
∏i−1n(di−1)!(n−2)!种情况。
其实最重要的不是构造prufer,而是性质三。。
性质3怎么来的?很简单的组合数学,序列一共有n-2个位置,挑出d[i]-1个给一个点,然后减去已被占有的位置,继续。
a
n
s
=
∏
i
−
1
n
C
d
[
i
]
−
1
s
u
m
=
(
n
−
2
)
!
∏
i
−
1
n
(
d
i
−
1
)
!
ans=\\prod_i-1^nC_d[i]-1^sum=\\frac(n-2)!\\prod_i-1^n(d_i-1)!
ans=i−1∏nCd[i]−1sum=∏i−1n(di−1)!(n−2)!
其中:
s
u
m
=
n
−
2
−
∑
j
−
1
i
−
1
(
d
[
j
]
−
1
)
sum=n-2-\\sum_j-1^i-1(d[j]-1)
sum=n−2−j−1∑i−1(d[j]−1)
因为数目太大,当没有取模时,我们其实常常使用第一个式子,而不是第二个(会溢出)。
另外,如果度数序列之和不为2n-2。即为无解。(基础性质了)换做和purfer序列比较符合的结论就是
:一个度数序列有解,当且仅当
∑
i
−
1
n
(
d
[
i
]
−
1
)
=
=
n
−
2
\\sum_i-1^n(d[i]-1) ==n-2
∑i−1n(d[i]−1)==n−2。
P2290 [HNOI2004]树的计数
这个题就是用第一个公式推定的,因为用第二个会溢出,当然,如果有大佬质因子分解也可做QAQ。
下面是ac代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <bitset>
#include <array>
#include <cctype>
#include <time.h>
#pragma GCC optimize(2)
void read_f() freopen("1.in", "r", stdin); freopen("1.out", "w", stdout);
void fast_cin() std::ios::sync_with_stdio(false); std::cin.tie();
void run_time() std::cout << "ESC in : " << clock() * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl;
template <typename T>
bool bacmp (const T & a, const T & b) return a > b;
template <typename T>
bool pecmp (const T & a, const T & b) return a < b;
#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr(x, y) (make_pair((x), (y)))
#define pb(x) push_back(x);
using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const int N = 156;
ll C[N][N];
ll d[N];
void init(int n)
for (int i = 0; i <= n; i++)
C[i][0] = C[i][i] = 1;
for (int j = 1; j <= i; j++)
C[i][j] = C[i-1][j] + C[i-1][j-1];
int main()
int n; scanf("%d", &n);
init(n);
ll sum = 0;
for (int i = 1; i <= n; i++)
scanf("%d", &d[i]), sum += d[i] - 1;
if (n == 1) puts(d[1] ? "0" : "1"); return 0;
if (sum != n - 2) puts("0"); return 0;
ll ans = 1;
for (int i = 1; i <= n; i++)
if (d[i] == 0) puts("0"); return 0;
ans = ans * C[sum][d[i] - 1], sum -= d[i] - 1;
printf("%lld\\n", ans);
return 0;
以上是关于Purfer序列的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ_1005_ [HNOI2008]_明明的烦恼_(组合数学+purfer_sequence+高精度+分解因数)
vijos2054 SDOI2019 热闹的聚会与尴尬的聚会