求序列k(10^18)次前缀与差分

题目链接:

登录—专业IT笔试面试备考平台_牛客网

题意:

给长度为n(n\leq 10^5)的序列a,求其k(-10^{18}\leq k\leq 10^{18})次前缀和,前缀和对998244353取模。

前置知识:

参考自右链接,写的超棒:数据结构专题班:1 前缀和与差分_wavecho的博客-CSDN博客

如果序列做前缀和取的模数大于序列的长度,前缀和序列会以 模数长度 的循环节循环出现。
题目中的模数大于序列长度,可以对负数的k (差分) 取模后转化为前缀和。

代码: 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;
const ll mod = 998244353;
 
namespace NTT//取模ntt模板
{
const long long g = 3;
const long long p = 998244353;
long long wn[35];
long long pow2(long long a, long long b)
{
    long long res = 1;
    while (b)
    {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}
void getwn()
{
    for (int i = 0; i < 25; i++) wn[i] = pow2(g, (p - 1) / (1LL << i));
}
void ntt(long long *a, int len, int f)
{
    long long i, j = 0, t, k, w, id;
    for (i = 1; i < len - 1; i++)
    {
        for (t = len; j ^= t >>= 1, ~j & t;);
        if (i < j) swap(a[i], a[j]);
    }
    for (i = 1, id = 1; i < len; i <<= 1, id++)
    {
        t = i << 1;
        for (j = 0; j < len; j += t)
        {
            for (k = 0, w = 1; k < i; k++, w = w * wn[id] % p)
            {
                long long x = a[j + k], y = w * a[j + k + i] % p;
                a[j + k] = (x + y) % p;
                a[j + k + i] = (x - y + p) % p;
            }
        }
    }
    if (f)
    {
        for (i = 1, j = len - 1; i < j; i++, j--) swap(a[i], a[j]);
        long long inv = pow2(len, p - 2);
        for (i = 0; i < len; i++) a[i] = a[i] * inv % p;
    }
}
void mul(long long *a, long long *b, int l1, int l2)
{
	getwn();
    int len, i;
    for (len = 1; len <= l1 + l2; len <<= 1);
    for (i = l1 + 1; i <= len; i++) a[i] = 0;
    for (i = l2 + 1; i <= len; i++) b[i] = 0;
    ntt(a, len, 0); ntt(b, len, 0);
    for (i = 0; i < len; i++) a[i] = a[i] * b[i] % p;
    ntt(a, len, 1);
}
};
 
 
 
ll n,k;
ll a[maxn];
ll inv[maxn];
ll ki[maxn];

//线性时间求逆元
void init(long long n)
{
    inv[0] = inv[1] = 1;
    for (long long i = 2; i <= n; i++)
    {
        inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
    }
    return;
}
 
void get_ki(long long k, int len)
{
    k = (k % mod + mod) % mod;
    ki[0] = 1;
    for (int i = 1; i < len; ++i)
    {
        ki[i] = ki[i - 1] * inv[i] % mod * ((k + i - 1) % mod) % mod;
    }
}
 
int main(){
    init(100000);
    scanf("%lld%lld",&n,&k);
    get_ki(k,n);
    for(int i = 0;i < n;i++){
        scanf("%lld",&a[i]);
    }
    NTT::mul(a,ki,n,n);
    /*
    a数组为初始序列
    ki为求k次前缀和的系数[1,3,6,10...]
    */
    for(int i = 0;i < n;i++)
        printf("%lld%c",a[i],i == n - 1?'\n':' ');
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值