洛谷P5206 数树
题意:
task0,给定两棵树T1,T2,取它们公共边(两端点相同)加入一张新的图,记新图连通块个数为x,求yx。
task1,给定T1,求所有T2的task0之和。
task2,求所有T1的task1之和。
解:y = 1的时候特殊处理,就是总方案数。
task0,显然按照题意模拟即可。
task1,对某个T2,设有k条边相同,那么连通块数就是n - k。要求的就是
对于每个T2,前面yn都是一样的,所以直接去掉,最后乘上即可。关注后面这个东西怎么求。令y' = 1/y,E是公共边集。
注意到
这里下式是枚举边集E的子集S,对每个的子集贡献求和。
注意上式先枚举a再求组合数,相当于枚举在边集里选a条边,然后枚举选哪a条边。也就是枚举子集。
也就是下面这段话想表达的。摘自Cyhlnj。里面还提到了一个n3的矩阵树定理做法,神奇。
容斥写法在下不会T_T
下一步,把S提前枚举,在不同的E中同一个S的贡献总是相同的。考虑一个S会对哪些E产生贡献,也就是它的贡献会被计算多少次。
这|S|条边会形成若干个连通块。这些连通块通过加上一些边可以形成树。这些新边没有任何限制,于是就是连通块的生成树计数。
这里又有若干种推法......个人认为最简单的是利用prufer序列求解。
摘自Joker_69。
令z = y' - 1,m = 边集为S时的连通块数 = n - |S|,第i号连通块有ai个点,于是我们的答案变成了这样:
这个东西怎么求呢?注意到在T1中选择任意的边集S等价于把T1划分为若干个连通块,用这些边连起来。于是就考虑树形DP。
这后面这个求积,要乘上每个连通块的大小,有个暴力是f[x][i]表示x为根,x所在连通块大小为i的所有方案权值和。
n2过不了,于是换个思路就是在每个连通块中选一个关键点的方案数。
因为是以联通块为依据DP,所以变形一下,加上之前忽略的yn,我们有:
于是状态设计有f[x][0/1]表示在以x为根的子树中,x所在连通块是否选择了关键点的所有方案权值之和。
每个联通块的贡献是z-1n,且我们只在关键点被选出来的那一瞬间计算这个联通块的贡献。
同时由于每个连通块的贡献要乘起来,那么所有方案之和还是要乘起来,等价于每个方案两两求积再相加。
口胡了半天还是写一下方程吧。
f[x][] = ;
f[x][] = invz * n % MO; LL t0 = f[x][] * f[y][] % MO + f[x][] * f[y][] % MO;
LL t1 = f[x][] * f[y][] % MO + f[x][] * f[y][] % MO + f[x][] * f[y][] % MO;
f[x][] = t0 % MO;
f[x][] = t1 % MO;
task2:问题变得严重起来......
跟task1一样,对于某个T1和T2的组合,它的贡献仍能拆成它的子集的贡献。
设g(E)为给定E这个边集之后的生成树个数,由task1可得g(E) = nm-2∏ai。
枚举E为一定相同的边集,剩下的边随便连。那么对于T1的g(E)种情况,T2都有g(E)种情况。
所以E这个边集的贡献为z|E|g2(E)。
m还是连通块数,我们暴力展开g(E),并把与m有关的项放到∏里面,无关的提到外面,令r = n2/z,那么答案就是:
接下来这一步很毒瘤...我们考虑这个式子有什么实际意义。
前面的边集把这个图分成了若干个森林。每个连通块一定是树。后面相当于给每个连通块赋了ai2r的权值,并把权值乘起来作为这个边集的贡献。
设fi为大小为i的树的贡献,对应的EGF是F(x),gi为大小为i的图的贡献,对应的EGF是G(x)
那么有这样一个式子:G(x) = eF(x)
考虑fi是多少:每种树的权值都是i2r,一共有ii-2种树,贡献加起来是iir。
这样就对F(x)做exp,然后拿G(x)的第n项出来搞一搞就是答案了。
多项式操作别写错了......我一开始WA了20分是因为有这样的一句话:n * n
然后两个n乘起来爆int了......这题神奇的一批...
#include <cstdio>
#include <algorithm>
#include <cstring> typedef long long LL;
const int N = ;
const LL MO = ; inline LL qpow(LL a, LL b) {
LL ans = ;
a %= MO;
while(b) {
if(b & ) ans = ans * a % MO;
a = a * a % MO;
b = b >> ;
}
return ans;
} struct Edge {
int nex, v;
}edge[N << ]; int tp; int n, e[N];
LL Y, z; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} namespace t0 {
int fa[N];
void DFS(int x, int f) {
fa[x] = f;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == f) continue;
DFS(y, x);
}
return;
}
inline void solve() {
if(Y == ) {
puts("");
return;
}
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
DFS(, );
int k = ;
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
if(fa[x] == y || fa[y] == x) {
k++;
}
}
LL ans = qpow(Y, n - k);
printf("%lld\n", ans);
return;
}
} namespace t1 {
LL f[N][], invz;
void DFS(int x, int father) {
f[x][] = ;
f[x][] = invz * n % MO;
//printf("x = %d fa = %d \n", x, father);
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
//printf("y = %d \n", y);
if(y == father) continue;
DFS(y, x);
LL t0 = f[x][] * f[y][] % MO + f[x][] * f[y][] % MO;
LL t1 = f[x][] * f[y][] % MO + f[x][] * f[y][] % MO + f[x][] * f[y][] % MO;
f[x][] = t0 % MO;
f[x][] = t1 % MO;
}
//printf("X = %d f[x][0] = %lld f[x][1] = %lld \n", x, f[x][0], f[x][1]);
return;
}
inline void solve() {
if(Y == ) {
LL ans = qpow(n, n - );
printf("%lld\n", ans);
return;
}
z = qpow(Y, MO - ); z = (z - + MO) % MO;
invz = qpow(z, MO - );
for(int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y); add(y, x);
}
DFS(, );
LL ans = f[][] * qpow(n, MO - ) % MO * qpow(z, n) % MO * qpow(Y, n) % MO;
printf("%lld\n", ans);
return;
}
} namespace t2 { typedef LL arr[N * ];
const LL G = ; int r[N * ];
arr A, B, a, b, inv_t, exp_t, ln_t, ln_t2;
LL pw[N]; inline void prework(int n) {
static int R = ;
if(R == n) return;
R = n;
int lm = ;
while(( << lm) < n) lm++;
for(int i = ; i < n; i++) {
r[i] = (r[i >> ] >> ) | ((i & ) << (lm - ));
}
return;
} inline void NTT(LL *a, int n, int f) {
prework(n);
for(int i = ; i < n; i++) {
if(i < r[i]) std::swap(a[i], a[r[i]]);
}
for(int len = ; len < n; len <<= ) {
LL Wn = qpow(G, (MO - ) / (len << ));
if(f == -) Wn = qpow(Wn, MO - );
for(int i = ; i < n; i += (len << )) {
LL w = ;
for(int j = ; j < len; j++) {
LL t = a[i + len + j] * w % MO;
a[i + len + j] = (a[i + j] - t) % MO;
a[i + j] = (a[i + j] + t) % MO;
w = w * Wn % MO;
}
}
}
if(f == -) {
LL inv = qpow(n, MO - );
for(int i = ; i < n; i++) {
a[i] = a[i] * inv % MO;
}
}
return;
} void Inv(const LL *a, LL *b, int n) {
if(n == ) {
b[] = qpow(a[], MO - );
b[] = ;
return;
}
Inv(a, b, n >> );
/// ans = b[i] * (2 - a[i] * b[i])
memcpy(A, a, n * sizeof(LL)); memset(A + n, , n * sizeof(LL));
memcpy(B, b, n * sizeof(LL)); memset(B + n, , n * sizeof(LL));
NTT(A, n << , ); NTT(B, n << , );
for(int i = ; i < (n << ); i++) b[i] = B[i] * ( - A[i] * B[i] % MO) % MO;
NTT(b, n << , -);
memset(b + n, , n * sizeof(LL));
return;
} inline void getInv(const LL *a, LL *b, int n) {
int len = ;
while(len < n) len <<= ;
memcpy(inv_t, a, n * sizeof(LL)); memset(inv_t + n, , (len - n) * sizeof(LL));
Inv(inv_t, b, len);
memset(b + n, , (len - n) * sizeof(LL));
return;
} inline void der(const LL *a, LL *b, int n) {
for(int i = ; i < n - ; i++) {
b[i] = a[i + ] * (i + ) % MO;
}
b[n - ] = ;
return;
} inline void ter(const LL *a, LL *b, int n) {
for(int i = n - ; i >= ; i--) {
b[i] = a[i - ] * qpow(i, MO - ) % MO;
}
b[] = ;
return;
} inline void getLn(const LL *a, LL *b, int n) {
getInv(a, ln_t, n);
der(a, ln_t2, n);
int len = ;
while(len < * n) len <<= ;
memset(ln_t + n, , (len - n) * sizeof(LL));
memset(ln_t2 + n, , (len - n) * sizeof(LL));
NTT(ln_t, len, ); NTT(ln_t2, len, );
for(int i = ; i < len; i++) b[i] = ln_t[i] * ln_t2[i] % MO;
NTT(b, len, -);
memset(b + n, , (len - n) * sizeof(LL));
ter(b, b, n);
return;
} void Exp(const LL *a, LL *b, int n) {
if(n == ) {
b[] = ;
b[] = ;
return;
}
Exp(a, b, n >> );
/// ans = b * (1 + a - ln b)
getLn(b, exp_t, n);
for(int i = ; i < n; i++) A[i] = (a[i] - exp_t[i]) % MO;
A[] = (A[] + ) % MO;
memset(A + n, , n * sizeof(LL));
memcpy(B, b, n * sizeof(LL)); memset(B + n, , n * sizeof(LL));
NTT(A, n << , ); NTT(B, n << , );
for(int i = ; i < (n << ); i++) b[i] = A[i] * B[i] % MO;
NTT(b, n << , -);
memset(b + n, , n * sizeof(LL));
return;
} inline void getExp(const LL *a, LL *b, int n) {
int len = ;
while(len < n) len <<= ;
Exp(a, b, len);
memset(b + n, , (len - n) * sizeof(LL));
return;
} inline void solve() {
if(Y == ) {
LL t = qpow(n, n - );
printf("%lld\n", t * t % MO);
return;
} LL z = (qpow(Y, MO - ) - ) % MO;
LL r = 1ll * n * n % MO * qpow(z, MO - ) % MO; pw[] = ;
for(int i = ; i <= n; i++) {
pw[i] = pw[i - ] * i % MO;
a[i] = qpow(i, i) * r % MO * qpow(pw[i], MO - ) % MO;
}
getExp(a, b, n + );
LL ans = b[n] * pw[n] % MO;
ans = ans * qpow(Y, n) % MO * qpow(z, n) % MO * qpow(n, MO - ) % MO;
printf("%lld\n", (ans + MO) % MO);
return;
}
} int main() { int f;
scanf("%d%lld%d", &n, &Y, &f);
if(f == ) {
t0::solve();
return ;
}
if(f == ) {
t1::solve();
return ;
}
t2::solve();
return ;
}
AC代码
以蒟蒻视角写了题解,以后还要继续努力!
感谢:
洛谷P5206 数树的更多相关文章
- 洛谷 P5206: bzoj 5475: LOJ 2983: [WC2019] 数树
一道技巧性非常强的计数题,历年WC出得最好(同时可能是比较简单)的题目之一. 题目传送门:洛谷P5206. 题意简述: 给定 \(n, y\). 一张图有 \(|V| = n\) 个点.对于两棵树 \ ...
- 洛谷1087 FBI树 解题报告
洛谷1087 FBI树 本题地址:http://www.luogu.org/problem/show?pid=1087 题目描述 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全 ...
- 洛谷P3018 [USACO11MAR]树装饰Tree Decoration
洛谷P3018 [USACO11MAR]树装饰Tree Decoration树形DP 因为要求最小,我们就贪心地用每个子树中的最小cost来支付就行了 #include <bits/stdc++ ...
- 洛谷 P5206 - [WC2019]数树(集合反演+NTT)
洛谷题面传送门 神仙多项式+组合数学题,不过还是被我自己想出来了( 首先对于两棵树 \(E_1,E_2\) 而言,为它们填上 \(1\sim y\) 使其合法的方案数显然是 \(y\) 的 \(E_1 ...
- 洛谷P3372线段树1
难以平复鸡冻的心情,虽然可能在大佬眼里这是水题,但对蒟蒻的我来说这是个巨大的突破(谢谢我最亲爱的lp陪我写完,给我力量).网上关于线段树的题解都很玄学,包括李煜东的<算法竞赛进阶指南>中的 ...
- NOIP2017提高组Day2T3 列队 洛谷P3960 线段树
原文链接https://www.cnblogs.com/zhouzhendong/p/9265380.html 题目传送门 - 洛谷P3960 题目传送门 - LOJ#2319 题目传送门 - Vij ...
- 洛谷P3703 [SDOI2017]树点涂色(LCT,dfn序,线段树,倍增LCA)
洛谷题目传送门 闲话 这是所有LCT题目中的一个异类. 之所以认为是LCT题目,是因为本题思路的瓶颈就在于如何去维护同颜色的点的集合. 只不过做着做着,感觉后来的思路(dfn序,线段树,LCA)似乎要 ...
- 洛谷P3830 随机树(SHOI2012)概率期望DP
题意:中文题,按照题目要求的二叉树生成方式,问(1)叶平均深度 (2)树平均深度 解法:这道题看完题之后完全没头绪,无奈看题解果然不是我能想到的qwq.题解参考https://blog.csdn.ne ...
- 洛谷 P3714 - [BJOI2017]树的难题(点分治)
洛谷题面传送门 咦?鸽子 tzc 竟然来补题解了?incredible( 首先看到这样类似于路径统计的问题我们可以非常自然地想到点分治.每次我们找出每个连通块的重心 \(x\) 然后以 \(x\) 为 ...
随机推荐
- Git push 时如何避免出现 "Merge branch 'master' of ..."
在使用 Git 的进行代码版本控制的时候,往往会发现在 log 中出现 "Merge branch 'master' of ..." 这句话,如下图所示.日志中记录的一般为开发过程 ...
- 快速零配置迁移 API 适配 iOS 对 IPv6 以及 HTTPS 的要求
本文快速分享一下快速零配置迁移 API 适配 iOS 对 IPv6 以及 HTTPS 的要求的方法,供大家参考. 原文发表于我的技术博客 零配置方案 最新的苹果审核政策对 API 的 IPv6 以及 ...
- 高级程序设计JavaScript
JavaScript简介 一.因何而生: 方便 表单验证操作(输入验证器) 1995年 2月 Netscape 布兰登 艾奇 JavaScript是一种专门为与网页交互而设计的脚本语言. 二.实现组成 ...
- M2阶段团队贡献分
根据任务完成情况与之前的评分标准,我们给组员分数如下: 团队成员 最终得分 程刚 51 李睿琦 53 刘丽萍 50 刘宇帆 48 王力民 47 马佐霖 49 左少辉 52
- 个人博客作业Week2(代码规范,代码复审)
Q:是否需要有代码规范 首先我们来搞清楚什么是“代码规范”,它和“代码风格”又有什么关系.依据个人的审美角度,我可能更喜欢在函数与函数之间空出一行,可能在命名习惯和代码注释上更加的internatio ...
- Leetcode 712. 两个字符串的最小ASCII删除和
题目描述: https://leetcode-cn.com/problems/minimum-ascii-delete-sum-for-two-strings/ 解题思路: 也是典型的dp问题.利用二 ...
- HDU 2075 A|B?
http://acm.hdu.edu.cn/showproblem.php?pid=2075 Problem Description 正整数A是否能被正整数B整除,不知道为什么xhd会研究这个问题,来 ...
- Oracle18c Exadata 版本安装介质安装失败。
下载下来的介质安装失败 白费一早上的功夫.. 一会儿问问云和恩墨的人呢.. INFO: [-- ::] Skipping line: 复制数据库文件 INFO: [-- ::] Skipping li ...
- [读书笔记]Linux命令行与shell编程读书笔记03 文件系统等
1. 文件系统的种类 ext ext2 ext3 ext4 JFS XFS 其中ext3 开始支持journal日志模式 与raid卡类似 有 数据模式 排序模式 以及回写模式 数据模式最安全 回写 ...
- [书摘]图解HTTP 状态码
状态码类别: 1XX informational 信息性状态码 2XX Suess 成功状态码 3XX Redirection 重定向状态码 4XX Client error 客户端错误状态码 5 ...