[航海协会]树

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[航海协会]树相关的知识,希望对你有一定的参考价值。

题目概述


题解

首先看到这道题,一种比较显然的想法是通过生成函数来表我们的方案数。
我们记 f f f表示根节点权值为 n n n的狄利克雷生成函数, p = ∑ [ ∃ j , a j ∣ i ] x i p=\\sum [\\exists j,a_j|i]x^i p=[j,aji]xi,也就是叶子上的狄利克雷生成函数,容易得到转移式:
f = p + ∑ i = 2 ∞ f i = p + f 2 1 − f f=p+\\sum_i=2^\\inftyf^i=p+\\fracf^21-f f=p+i=2fi=p+1ff2答案就是 f f f的前 n n n项系数和,但看上去这东西不太好解的样子,我们不妨考虑将 f f f表示成关于 p p p的函数 G ( p ) G(p) G(p),那么可以得到
G = p + G 2 1 − G 2 G 2 − ( p + 1 ) G + p = 0 G = ( p + 1 ) ± p 2 − 6 p + 1 4 G=p+\\fracG^21-G\\\\ 2G^2-(p+1)G+p=0\\\\ G=\\frac(p+1)\\pm\\sqrtp^2-6p+14 G=p+1GG22G2(p+1)G+p=0G=4(p+1)±p26p+1 这样的话,我们就可以考虑通过多项式开根求出我们 G G G关于 p p p的多项式。
事实上,我们只需要保留 G G G中的前 log ⁡ min ⁡ a n \\log_\\min an logminan次项,再之后的显然前 n n n项的和都是 0 0 0了。
这样我们关于 G G G的计算就比较简单了,同时也成功地把不同次项的 p k p^k pk独立了出来。
考虑每个 p k p^k pk产生贡献,显然是在 G G G中的系数乘上 p k p^k pk里的前缀和。
我们前面是把 p p p当成一个自变量来看的,所以前面关于 p k p^k pk系数的计算都是依照普通多项式乘法的方式进行的。
但之后求 p k p^k pk的前 n n n项是需要涉及到 p p p自己卷上 k k k次的 p k p^k pk,这部分采用的实际上都是狄利克雷卷积,考虑怎么对这个式子算前 n n n项的和。

显然,一个比较经典的做法是杜教筛。
我们用 p p p卷上 p k − 1 p^k-1 pk1得到 p k p^k pk的前缀和,有式子:
S k ( n ) = ∑ p ( i ) S k − 1 ( ⌊ n x ⌋ ) S^k(n)=\\sum p(i)S^k-1(\\lfloor\\fracnx\\rfloor) Sk(n)=p(i)Sk1(⌊xn⌋)显然,后面部分是可以通过数论分块快速计算的。
最开始的 S 1 ( n ) S^1(n) S1(n)的每个块位置的前缀和通过容斥就可以 O ( 2 m n ) O\\left(2^m\\sqrtn\\right) O(2mn )地算出,之后就在每一层对每个前缀和数论分块计算。
这样的话时间复杂度是 O ( 2 m ( n + m ) + n 3 4 log ⁡ n ) O\\left(2^m(\\sqrtn+m)+n^\\frac34\\log n\\right) O(2m(n +m)+n43logn)的,实测会 T T T

没事,杜教筛一个经典的优化方法就是交小部分的前缀和通过筛子预处理出来。
显然,我们这里是不太能线性筛,但仍能做到 O ( n ln ⁡ n ) O\\left(n\\ln n\\right) O(nlnn)的调和级数复杂度欧拉筛预处理,也就是暴力将 p p p p k − 1 p^k-1 pk1狄利克雷卷积。
于是我们就根号分治一下,小于 B B B的部分每层都做一次欧拉筛,大于 B B B的部分用我们上面提及的方法杜教筛。
这样就能将时间复杂度做到 O ( 2 m ( n + m ) + n 2 3 log ⁡ 4 3 n ) O\\left(2^m(\\sqrtn+m)+n^\\frac23\\log^\\frac43n\\right) O(2m(n +m)+n32log34n),不是严格的,因为约到后面非 0 0 0的部分也就越少了,实测跑得比官方题解做法快。

源码

直接把多项式开根后的结果打下来了(懒

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,int> pii;
#define MAXN 200005
#define MAXM (1<<8)+5
#define MAXT 3000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
const int mo=1e9+7;
template<typename _T>
void read(_T &x)
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
    while('0'<=s&&s<='9')x=(x<<3[航海协会]树

[航海协会]树

[航海协会]树

[航海协会]树

[航海协会]字符串

[航海协会]基因切割