51NOD 序列求和 V5题解

本文详细解析了51NOD序列求和V5题目,涉及到数论概念和完全积性函数的线性筛预处理。当R=0时,答案为0;R=1时,通过二项式反演求解;R≠1时,利用特定的多项式关系式进行计算。文章还证明了一个关于求和的引理,有助于解题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  被数论くん搞得头大的本宝宝来写一发数论题~
  51NOD 序列求和 V5
  若 R0  ,那么显然答案就为0.
  接下来,我们用符号 f k (n)  代表我们所要求的值。首先我们要预处理 f k (1),,f k (k+1)  的值,由于 x k   是一个完全积性函数,利用线性筛,这一预处理可以很容易在 O(k)  的时间内完成。
  若 R1  ,则原问题转化为求  n i=1 i k   的值,我们知道,这时 f k (n)  是关于 n  k+1 次多项式。同时,我们也知道, nk+1  时,每一个 C i n   都是关于 n  i 次多项式,并且它们线性无关,而 nk  时,可以 O(1)  直接查询。对于 nk+1  的情况,我们用 C 0 n ,,C k+1 n   k+2  个数来线性表出 f k (n)  ,设 f k (n)= k+1 i=0 C i n a i   ,根据二项式反演,有 a n = n i=0 (1) ni C i n f k (i) 
  这里先写一个引理:  k i=0 (1) i C i n =(1) k C k n1   ,这个很好证我就不写了。
  将 a n   代回原式得:

f k (n) = i=0 k+1 C i n  j=0 i (1) ij C j i f k (j)= i=0 k+1 f k (i) j=i k+1 (1) ji C j n C i j = i=0 k+1 f k (i)C i n  j=i k+1 (1) ji C ji ni = i=0 k+1 f k (i)C i n  j=0 ki+1 (1) j C j ni = i=0 k+1 (1) ki+1 C i n C ki+1 ni1 = i=0 k+1  0jk+1,ji nji!(ki+1)!   

  这样,分子和分母的部分都可以预处理出来,复杂度 O(k) 
  若 R≢1  ,有:

(R1)f k (n)f k (n) =(R1) i=1 n i k R i = i=1 n i k R i+1  i=1 n i k R i = i=1 n+1 (i1) k R i  i=1 n i k R i =n k R n+1  i=1 n  j=0 k1 (1) kj1 C j k i j R i =n k R n+1  j=0 k1 (1) kj+1 C j k f j (n)=n k R n+1  k1 j=0 (1) kj+1 C j k f j (n)R1   

  下面我们证明一个结论,对 f i (n),0ik,F i (n)Q i [x]  ,有 f i (n)=R n+1 F i (n)RF i (0) 

iif k (n) =0,()=k0,,k1=n k R n+1  k1 j=0 (1) kj+1 C j k f j (n)R1 =n k R n+1  k1 j=0 (1) kj+1 C j k [R n+1 F j (n)RF j (0)]R1 =R n+1 [n k  k1 j=0 (1) kj+1 C k j F j (n)]+R[ k1 j=0 (1) kj+1 C j k F j (0)]R1 F k (n)=n k  j=0 k1 (1) kj+1 C k j F j (n)  

  我们再来证明一个引理:对 0mk, k i=0 (1) i C i k C m i =0 

 i=0 k (1) i C i k C m i  = i=0 k (1) i C m k C im km =C m k  i=0 km (1) i+m C i km =C m k (1) m (11) (km) =0  

  在上一部分的讨论中,我们已经知道,作为一个 k  次多项式,F k (n) 可以表示为 C 0 n ,,C k n   的线性组合。设 F k (n)= k i=0 C i n a i   ,则
 i=0 k (1) i C i k F k (n) = i=0 k (1) i C i k  j=0 k C j i a j = j=0 k  i=0 k (1) i C i k C j i a j = j=0 k 0=0  

  我们又有:
(n+1) k R n+1 F k (n+1) =f k (n+1)f k (n)=R n+2 F k (n+1)R n+1 +RF k (0)=(n+1) k +F k (n)R   

  通过上面两个等式,我们就可以通过解一个一元一次方程来得到 F k (0),,F k (k)  的值,之后我们用拉格朗日插值公式就可以 O(k)  地得到 F k (n)  的值,再代回原式,这道题就解决了。

#include<bits/stdc++.h>
#define ll long long

const int N = 200010;
const int moder = 985661441;

int power(int a, ll index){
    int ans = 1;
    while (index){
        if (index & 1)
            ans = 1ll * ans * a % moder;
        a = 1ll * a * a % moder;
        index >>= 1;
    }
    return ans;
}

int min[N], x[N], f[N], F[N], prefix[N], suffix[N], fac[N], inv[N], t, k;
ll n, r;
std::vector <int> prime;

void calc(){
    prefix[0] = suffix[k + 1] = 1;
    for (int i = 1; i <= k + 1; ++ i){
        prefix[i] = 1ll * prefix[i - 1] * ((n - i + 1) % moder) % moder;
    }
    for (int i = k; i >= 0; -- i){
        suffix[i] = 1ll * suffix[i + 1] * ((n - i - 1) % moder) % moder;
    }
    int ans = 0;
    for(int i = 0; i <= k + 1; ++ i){
        int now = 1ll * f[i] * inv[i] % moder * inv[k - i + 1] % moder;
        now = 1ll * now * prefix[i] % moder * suffix[i] % moder;
        if ((k - i + 1) & 1)
            now = (moder - now) % moder;
        ans = (ans + now) % moder;
    }
    printf("%d\n", ans);
}

void _calc(){
    int inv_r = power(r, moder - 2);
    int sum_a = 1, sum_b = 0;
    prefix[0] = 1;
    suffix[0] = 0;
    for (int i = 1; i <= k + 1; ++ i){
        prefix[i] = 1ll * prefix[i - 1] * inv_r % moder;
        suffix[i] = 1ll * (suffix[i - 1] + x[i]) * inv_r % moder;
        int coe = 1ll * fac[k + 1] % moder * inv[i] % moder * inv[k + 1 - i] % moder;
        if (i & 1)
            coe = (moder - coe) % moder;
        sum_a = (sum_a + 1ll * coe * prefix[i]) % moder;
        sum_b = (sum_b + 1ll * coe * suffix[i]) % moder;
    }
    int _x = 1ll * (moder - sum_b) * power(sum_a, moder - 2) % moder;
    for (int i = 0; i <= k; ++ i)
        F[i] = (1ll * prefix[i] * _x + suffix[i]) % moder;
    prefix[0] = suffix[k] = 1;
    for (int i = 1; i <= k; ++ i){
        prefix[i] = 1ll * prefix[i - 1] * ((n - i + 1) % moder) % moder;
    }
    for (int i = k - 1; i >= 0; -- i){
        suffix[i] = 1ll * suffix[i + 1] * ((n - i - 1) % moder) % moder;
    }
    int ans = 0;
    for(int i = 0; i <= k; ++ i){
        int now = 1ll * F[i] * inv[k - i] % moder * inv[i] % moder;
        now = 1ll * now * prefix[i] % moder * suffix[i] % moder;
        if ((k - i) & 1)
            now = (moder - now) % moder;
        ans = (ans + now) % moder;
    }
    ans = (1ll * ans * power(r, n) - F[0] + moder) % moder;
    ans = 1ll * ans * r % moder;
    printf("%d\n", ans);
}

int main(){
    scanf("%d", &t);
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i < N; ++ i){
        fac[i] = 1ll * i * fac[i - 1] % moder;
        inv[i] = power(fac[i], moder - 2);
        if (!min[i]){
            min[i] = i;
            prime.push_back(i);
        }
        for (int j = 0; j < prime.size() && prime[j] <= i && prime[j] * i < N; ++ j)
            min[prime[j] * i] = prime[j];
    }
    while (t --){   
        scanf("%d%I64d%I64d", &k, &n, &r);
        r %= moder;
        if (r == 0){
            puts("0\n");
            continue;
        }
        x[1] = 1;
        int now = r;
        f[1] = r;
        for (int i = 2; i < k + 10; ++ i){
            now = 1ll * now * r % moder;
            if (min[i] == i)
                x[i] = power(i, k);
            else
                x[i] = 1ll * x[min[i]] * x[i / min[i]] % moder;
            f[i] = (f[i - 1] + 1ll * x[i] * now) % moder;
        }
        if (n < 1ll * (k + 10)){
            printf("%d\n", f[n]);
            continue;
        }
        if (r == 1){
            calc();
            continue;
        }
        _calc();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值