洛谷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\) 为 ...
随机推荐
- 安装zkpython出错
pip3 install zkpython==0.4.2 提示:zookeeper.c:20:23: 致命错误:zookeeper.h:没有那个文件或目录 解决: 1.是否安装python-devel ...
- Centos7.5部署MySQL5.7基于GTID主从复制+并行复制+半同步复制+读写分离(ProxySQL) 环境- 运维笔记 (完整版)
之前已经详细介绍了Mysql基于GTID主从复制的概念,原理和配置,下面整体记录下MySQL5.7基于GTID主从复制+并行复制+增强半同步复制+读写分离环境的实现过程,以便加深对mysql新特性GT ...
- restfull环境搭建-helloword(三)
原文地址:http://only81.iteye.com/blog/1689537 This section creates a CRUD (Create, Read, Update, Delete) ...
- M1事后分析汇报以及总结
一.设想和目标 1. 们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件主要是为了提供周边美食和菜谱查询功能,为“吃货”们提供便利.对典型用户和场景有清晰的描 ...
- LINUX内核分析第六周学习总结——进程的描述与创建
LINUX内核分析第六周学习总结--进程的描述与创建 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc ...
- <<浪潮之巅>>阅读笔记三
纵看世界,横看国内.我们国内也有很多很优秀的企业正在走向或者已经处于浪潮之巅.阿里巴巴.腾讯和百度这三巨头应该是我们计算机行业的龙头.但是 不得不说,在创新方面我们做的并不多,这是值得每一个从事计算机 ...
- 传参在mybatis的sql映射文件中正确获取
1.单个参数: 非自定义对象 传参:getStuById(Integer id): 取值:#{id} 单个基本类型参数,随便取值都行:#{ok} 对象: 传参:saveStudent(Student ...
- WebLogic: 内存溢出
每次部署完项目,重启Weblogic服务,总是遇到java.lang.OutOfMemoryError: PermGen space,其实物理内存也是够的,当时的解决方法就是多重启几次Weblogic ...
- Vue的filter属性
Vue.filter('sss',function(data){ //可以使用逻辑处理数据 data += '123'; return data*10; }) new Vue({ el:'#app', ...
- [转帖] linux下面 vim 数字键无法插入的解决办法
感谢原作者: https://blog.csdn.net/guoyuqi0554/article/details/11477597 这个问题困扰自己好久了.. 刚才解决了 rlwrap的问题 这会儿 ...