SCUT - 482 - 生成树上的点 - Prufer
没听说过这个东西。
洛谷也有这个,所以还是要去接触一些奇奇怪怪的知识才行。
https://www.luogu.org/problem/P2290
画了一个表。
1个点:
F[1]=1
2个点:
F[1]=1
F[2]=1
3个点:
F[1]=2/3
F[2]=1/3
F[3]=0
4个点:
F[1]=9/16
F[2]=4/16
F[3]=1/16
F[4]=0
没发现有什么规律,可能是真的要画到5才可以?
Prufer序列,用于对同一形态的无根树映射到序列。用来解决只跟度数有关的树的问题。
1.找度数为1的,且编号最小的点,把它的父亲加入Prufer序列,且把它删除。
然后会得到一个n-2个点的序列,其中出现元素的次数就是他的度数-1。
从Prufer恢复树:
2.取出Prufer的第一个节点x,把不在Prufer里的最小元素y,在x-y之间连边。
性质:
1.prufer序列与无根树一一对应。
2.度数为d_i的节点会在prufer序列中出现d_i-1次。
3.一个n个节点的完全图的生成树个数为n{n-2}。因为对于一个n个点的无根树,它的prufer序列长为n−2,而每个位置有n种可能性,因此可能的prufer序列有n种。很显然每一种无根树唯一对应完全图的一个生成树。
4.对于给定度数序列为d_i的一棵无根树共有 \(\frac{(n-2)!}{\prod_{i=1}^n(d_i-1)!}\) 种情况。由上面的性质可以知道,度数为d_i的节点会在prufer序列中出现d_i-1次。则就是要求出d_i-1个i的全排列个数。而上面那个式子就是可重全排列公式。(即全排列个数除以重复元素内部的全排列个数)
回到这一题,每种生成树对应一个Prufer序列,那么总共肯定是n{n-2}种生成树,其中F[1]肯定是不选1,那么就是(n-1),以此类推。
众所周知,幂函数是完全积性的。把它移动一下就可以了。
还卡内存,真的毒瘤。开多一个F数组拿来复制都不行。
事实上不需要这么强迫症,写个函数映射过去,或者直接在答案里面映射过去就可以了。
试试证明bitset在单个访问的时候对时间的浪费程度比bool大。
然而因为MOD是一个1e9+7,所以不会有任何比它小的数的幂次会MOD它为0,可以直接用G[i]来判断。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
int qpow(ll x, int n) {
ll res = 1;
while(n) {
if(n & 1)
res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}
const int MAXN = 5e7 + 500;
int G[MAXN];
int p[3100000], ptop;
//int pk[MAXN]
bool np[MAXN];
int N, L, R;
void sieve(int n) {
G[0] = 0;
G[1] = 1;
//pk[1]=1;
np[1] = 1;
for(int i = 2; i <= n; i++) {
if(!np[i]) {
p[++ptop] = i;
//pk[i] = i;
G[i] = qpow(i, N - 2);
}
for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; j++) {
np[t] = 1;
if(i % p[j]) {
//pk[t] = p[j];
G[t] = 1ll * G[i] * G[p[j]] % MOD;
} else {
//pk[t] = pk[i] * p[j];
//下面是积性
/*if(pk[t] == t) {
G[t] = qpow(N - t, N - 2);
} else {
G[t] = 1ll * G[pk[t]] * G[t / pk[t]] % MOD;
}*/
//其实G[i]是完全积性
G[t] = 1ll * G[i] * G[p[j]] % MOD;
break;
}
}
}
//printf("%d\n",ptop);
/*for(int i = 1; i <= N; ++i) {
F[i] = G[N - i];
//printf("F[%d]=%d\n", i, F[i]);
}*/
/*reverse(G, G + N + 1);
for(int i = 1; i <= N; ++i) {
G[i] += G[i - 1];
if(G[i]>=MOD)
G[i]-=MOD;
//printf("F[%d]=%d\n", i, F[i]);
}*/
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
while(~scanf("%d%d%d", &N, &L, &R)) {
if(N <= 2) {
printf("%d\n", R - L + 1);
continue;
}
sieve(N);
int ANS = 0;
for(int i = N - R; i <= N - L; ++i) {
ANS += G[i];
if(ANS >= MOD)
ANS -= MOD;
}
printf("%d\n", 1ll * ANS * qpow(qpow(N, N - 2), MOD - 2) % MOD);
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
int qpow(ll x, int n) {
ll res = 1;
while(n) {
if(n & 1)
res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}
const int MAXN = 5e7 + 5;
int G[MAXN];
int p[3100000], ptop;
int N, L, R;
void sieve(int n) {
G[0] = 0;
G[1] = 1;
for(int i = 2; i <= n; i++) {
if(!G[i]) {
p[++ptop] = i;
G[i] = qpow(i, N - 2);
}
for(int j = 1, t; j <= ptop && (t = i * p[j]) <= n; j++) {
G[t] = 1ll * G[i] * G[p[j]] % MOD;
if(!(i % p[j]))
break;
}
}
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
while(~scanf("%d%d%d", &N, &L, &R)) {
if(N <= 2) {
printf("%d\n", R - L + 1);
continue;
}
sieve(N);
int ANS = 0;
for(int i = N - R; i <= N - L; ++i) {
ANS += G[i];
if(ANS >= MOD)
ANS -= MOD;
}
printf("%d\n", 1ll * ANS * qpow(qpow(N, N - 2), MOD - 2) % MOD);
}
return 0;
}