博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SCUT - 482 - 生成树上的点 - Prufer
阅读量:4681 次
发布时间:2019-06-09

本文共 4050 字,大约阅读时间需要 13 分钟。

没听说过这个东西。

洛谷也有这个,所以还是要去接触一些奇奇怪怪的知识才行。

画了一个表。

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^{n-2}种。很显然每一种无根树唯一对应完全图的一个生成树。

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)^{n-2},以此类推。


众所周知,幂函数是完全积性的。把它移动一下就可以了。


还卡内存,真的毒瘤。开多一个F数组拿来复制都不行。

事实上不需要这么强迫症,写个函数映射过去,或者直接在答案里面映射过去就可以了。

试试证明bitset在单个访问的时候对时间的浪费程度比bool大。

然而因为MOD是一个1e9+7,所以不会有任何比它小的数的幂次会MOD它为0,可以直接用G[i]来判断。

#include
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
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;}

转载于:https://www.cnblogs.com/Yinku/p/11343412.html

你可能感兴趣的文章
学前班
查看>>
关于自关联1
查看>>
hdu-1814(2-sat)
查看>>
谷歌浏览器,添加默认搜索引擎的搜索地址
查看>>
数据结构化与保存
查看>>
为什么需要Docker?
查看>>
国内5家云服务厂商 HTTPS 安全性测试横向对比
查看>>
how to control project
查看>>
转 python新手容易犯的6个错误
查看>>
第四节 -- 列表
查看>>
决策树
查看>>
团队作业
查看>>
如何避免在简单业务逻辑上面的细节上面出错
查看>>
大型网站高并发的架构演变图-摘自网络
查看>>
8丶运行及总结
查看>>
WPF中使用USERCONTROL
查看>>
图片,base64 互转
查看>>
cache—主存—辅存三级调度模拟
查看>>
Java线程的定义
查看>>
Python-面向对象(组合、封装与多态)
查看>>