zoukankan      html  css  js  c++  java
  • LOJ#6713. 「EC Final 2019」狄利克雷 k 次根 加强版

    题目描述

    定义两个函数 (f, g: {1, 2, dots, n} ightarrow mathbb Z) 的狄利克雷卷积 (f * g) 为:

    [(f * g)(n) = sum_{d | n} f(d)g(frac nd) ]

    我们定义 (g = f^k)(k) 次幂为:

    [f^{k}=underbrace {f * dots * f} _{k~{ extrm {个}}} ]

    在本题中,我们想要解决这个问题的逆问题:给你 (g)(k),你需要找到一个函数 (f) 使得 (g = f^k)

    另外,保证 (g(1)=1),你需要保证 (f(1)=1)。所有的运算在 (mathbb F_p) 上进行,其中 (p = 998244353),这意味着狄利克雷卷积为 ((f*g)(n) = left(sum_{d|n} f(d)g(frac nd) ight) mod p)

    (nle 10^6)

    Sol

    定义一个数论函数 (f(n)) 的狄利克雷生成函数是 (F(x)=sum_{n=1}^infty f(n)n^x)

    注意这个东西不是关于 (x) 的多项式(一开始我当成了多项式),然而它可以做多项式乘法,由于这个 (a^xcdot b^x ightarrow (ab)^x),所以对于生成函数系数来说,相当于完成了两个数论函数的 狄利克雷卷积

    既然有类似多项式的性质,那么我们考虑 ln-exp 的方法求解 k 次根。

    根据多项式 ln 的推导,有 (B=ln A ightarrow B=int {A'over A})

    除法可以用 狄利克雷除法 实现,那么我们只需要支持 导数和积分 运算即可。

    考虑这个指数函数的求导法则 (displaystyle { ext{d} a^xover ext{d}x}=a^xln a)

    然而在正整数意义下没有 (ln) 这种运算,然后做法是找一个运算性质跟 (ln) 一样的东西来替代它,比如质因子个数函数 ( ext{cnt}_x)(相同的算作多个),因为它满足 (f(x)+f(y)=f(xy)) 这样的性质。

    于是可以实现一个对一个狄利克雷级数求对数函数的代码如下:

    void ln(int n, int *a)
    {
    	b[1] = 0;
    	for(int i = 2; i <= n; ++i)
    		b[i] = (ll)a[i] * cnt[i] % P;
    	for(int i = 2; i <= n; ++i)
    	{
    		for(int j = 2, k = i * j; k <= n; ++j, k += i)
    			b[k] = (b[k] - 1LL * b[i] * a[j]) % P;
    		b[i] = (1LL * b[i] * inv[cnt[i]] % P + P) % P;
    	}
    	for(int i = 1; i <= n; ++i)
    		a[i] = b[i];
    }
    

    由于 (B=A^{1/k}=exp {{ln A} over k}),那么只要先求 (ln)(exp) 即可。
    求指数函数也是类似的,整个代码如下,和暴力 (O(n^2)) 的普通多项式对数/指数函数非常相似。

    #include<stdio.h>
    
    typedef long long ll;
    const int N = 1000005, P = 998244353;
    
    int fexp(int a, int b)
    {
    	ll x = 1, o = a;
    	for(; b; b >>= 1, o = o * o % P)
    		if(b & 1) x = x * o % P;
    	return x;
    }
    
    int n, K, a[N], b[N], inv[128], cnt[N];
    bool np[N];
    
    void init()
    {
    	cnt[1] = 1;
    	for(int i = 2; i <= n; ++i)
    		if(!np[i])
    		{
    			cnt[i] = 1;
    			for(int j = 2, k = i * j; k <= n; j++, k += i)
    			{
    				np[k] = true;
    				if(!cnt[k] && cnt[j]) cnt[k] = cnt[j] + 1;
    			}
    		}
    	inv[1] = 1;
    	for(int i = 2; i < 128; ++i)
    		inv[i] = (ll)inv[P % i] * (P - P / i) % P;
    }
    
    void ln(int n, int *a)
    {
    	b[1] = 0;
    	for(int i = 2; i <= n; ++i)
    		b[i] = (ll)a[i] * cnt[i] % P;
    	for(int i = 2; i <= n; ++i)
    	{
    		for(int j = 2, k = i * j; k <= n; ++j, k += i)
    			b[k] = (b[k] - 1LL * b[i] * a[j]) % P;
    		b[i] = (1LL * b[i] * inv[cnt[i]] % P + P) % P;
    	}
    	for(int i = 1; i <= n; ++i)
    		a[i] = b[i];
    }
    
    void exp(int n, int *a)
    {
    	for(int i = 2; i <= n; ++i)
    		b[i] = (ll)a[i] * cnt[i] % P, a[i] = 0;
    	a[1] = 1;
    	for(int i = 1; i <= n; ++i)
    	{
    		a[i] = 1ll * a[i] * inv[cnt[i]] % P;
    		for(int j = 2, k = i * j; k <= n; ++j, k += i)
    			a[k] = (a[k] + 1LL * a[i] * b[j]) % P;
    	}
    }
    
    int main()
    {
    	scanf("%d %d", &n, &K);
    	for(int i = 1; i <= n; ++i)
    		scanf("%d", a + i);
    	init();
    	K = fexp(K, P - 2);
    	ln(n, a);
    	for(int i = 1; i <= n; ++i) a[i] = 1ll * a[i] * K % P;
    	exp(n, a);
    	for(int i = 1; i <= n; ++i) printf("%d%c", a[i], " 
    "[i == n]);
    	return 0;
    }
    
  • 相关阅读:
    BZOJ2435 NOI2011道路修建
    BZOJ2431 HAOI2009逆序对数列(动态规划)
    BZOJ2456 mode
    BZOJ2324 ZJOI2011营救皮卡丘(floyd+上下界费用流)
    BZOJ2303 APIO2011方格染色(并查集)
    BZOJ2299 HAOI2011向量(数论)
    BZOJ2169 连边(动态规划)
    BZOJ2159 Crash的文明世界(树形dp+斯特林数)
    洛谷 P1306 斐波那契公约数 解题报告
    洛谷 P2389 电脑班的裁员 解题报告
  • 原文地址:https://www.cnblogs.com/bestwyj/p/12327701.html
Copyright © 2011-2022 走看看