【XSY2772】数列 特征多项式 数学

题目描述

  给你一个数列:
\[ f_n=\begin{cases} a^n&1\leq n\leq k\\ \sum_{i=1}^k(a-1)f_{n-i}&n>k \end{cases} \]
  记\(g_i\)为当\(k=i\)\(f_n\)的值,求
\[ \sum_{i=1}^mg_i\times {19260817}^i \]
  对于\(60\%\)的数据:\(m\leq 200,n\leq {10}^9\)

  对于另外\(40\%\)的数据:\(m\leq {10}^9,n\leq 3\times {10}^6\)

题解

第一部分

  直接按常系数线性递推的通用做法来做。

  可以不用FFT。

  时间复杂度:\(O(m^3\log n)\)\(O(m^2\log m\log n)\)

第二部分

  因为当\(i\geq n\)\(g_i=a^n\),所以我们只需要求\(g_1\ldots g_{n-1}\)
\[ \begin{align} f_n&=af_{n-1}-(a-1)f_{n-m-1}\\ F(x)&=axF(x)-(a-1)x^{k+1}F(x)+ax-ax^{k+1}\\ (1-ax+(a-1)x^{k+1})F(x)&=ax-ax^{k+1}\\ F(x)&=\frac{ax-ax^{k+1}}{1-ax+(a-1)x^{k+1}}\\ &=(ax-ax^{k+1})\sum_{i=0}^\infty\sum_{j=0}^i\binom{i}{j}{(1-a)}^jx^{j(k+1)}a^{i-j}x^{i-j}\\ \end{align} \]
  记
\[ G(x)=\sum_{i=0}^\infty\sum_{j=0}^i\binom{i}{j}{(1-a)}^jx^{j(k+1)}a^{i-j}x^{i-j}\\ \]
  那么
\[ \begin{align} F(x)&=(ax-ax^{k+1})G(x)\\ [x^n]F(x)&=a[x^{n-1}]G(x)-a[x^{n-k-1}]G(x)\\ [x^n]G(x)&=[x^n]\sum_{i=0}^\infty\sum_{j=0}^i\binom{i}{j}{(1-a)}^ja^{i-j}x^{jk+i}\\ &=\sum_{j}\sum_{i=n-jk}\binom{i}{j}{(1-a)}^ja^{i-j}\\ &=\sum_{j}\binom{n-jk}{j}{(1-a)}^ja^{n-j(k+1)}\\ \end{align} \]
  观察到对于所有的\(k\)\(j\)的取值总共有\(O(n\log n)\)种,所以可以暴力枚举\(k,j\)

  时间复杂度:\(O(n\log n+\log m)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
#include<iostream>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int rd()
{
    int s=0,c;
    while((c=getchar())<'0'||c>'9');
    s=c-'0';
    while((c=getchar())>='0'&&c<='9')
        s=s*10+c-'0';
    return s;
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
const ll p=998244353;
const ll vv=19260817;
ll fp(ll a,ll b)
{
    ll s=1;
    for(;b;b>>=1,a=a*a%p)
        if(b&1)
            s=s*a%p;
    return s;
}
int n,k,m;
ll pw[100010];
ll c[510];
ll d[510];
ll e[510];
int len;
void mul()
{
    static ll f[510];
    for(int i=0;i<=2*len;i++)
        f[i]=0;
    for(int i=0;i<len;i++)
        for(int j=0;j<len;j++)
            f[i+j]=(f[i+j]+d[i]*d[j])%p;
    for(int i=0;i<2*len;i++)
        d[i]=f[i];
}
void mod()
{
    for(int i=2*len;i>=len;i--)
        if(d[i])
        {
            ll v=d[i];
            for(int j=0;j<=len;j++)
                d[i-len+j]=(d[i-len+j]-v*c[j])%p;
        }
}
void pow(int n)
{
    if(!n)
        return;
    pow(n>>1);
    mul();
    if(n&1)
    {
        for(int i=2*len;i>=1;i--)
            d[i]=d[i-1];
        d[0]=0;
    }
    mod();
}
ll calc1(int x)
{
    len=x;
    memset(d,0,sizeof d);
    d[0]=1;
    c[x]=1;
    for(int i=0;i<x;i++)
        c[i]=-k+1;
    pow(n-1);
    ll ans=0;
    for(int i=1;i<=x;i++)
        ans=(ans+pw[i]*d[i-1])%p;
    return ans;
}
void solve1()
{
    ll ans=0;
    pw[0]=1;
    for(int i=1;i<=m;i++)
        pw[i]=pw[i-1]*k%p;
    for(int i=m;i>=1;i--)
        ans=(ans+calc1(i))*vv%p;
    ans=(ans+p)%p;
    printf("%lld\n",ans);
}
int fac[3000010];
int inv[3000010];
int ifac[3000010];
int s1[3000010];
int s2[3000010];
int getc(int x,int y)
{
    return (ll)fac[x]*ifac[y]%p*ifac[x-y]%p;
}
int gao(int n,int m)
{
    int s=0;
    for(int i=0;i*(m+1)<=n;i++)
        s=(s+(ll)fac[n-i*m]*s1[i]%p*s2[n-i*(m+1)])%p;
    return s;
}
void solve2()
{
    inv[1]=fac[0]=fac[1]=ifac[0]=ifac[1]=1;
    for(int i=2;i<=3000000;i++)
        fac[i]=(ll)fac[i-1]*i%p;
    s1[0]=1;
    for(int i=1;i<=3000000;i++)
        s1[i]=(ll)s1[i-1]*(1-k)%p;
    s2[0]=1;
    for(int i=1;i<=3000000;i++)
        s2[i]=(ll)s2[i-1]*k%p;
    for(int i=2;i<=3000000;i++)
    {
        inv[i]=(ll)-p/i*inv[p%i]%p;
        ifac[i]=(ll)ifac[i-1]*inv[i]%p;
        s1[i]=(ll)s1[i]*ifac[i]%p;
        s2[i]=(ll)s2[i]*ifac[i]%p;
    }
    ll ans=0;
    if(n<=m)
    {
        for(int i=n-1;i>=1;i--)
            ans=(ans+gao(n-1,i)-gao(n-i-1,i))*vv%p;
        ans=ans*k%p;
        ll v=fp(k,n)*(fp(vv,m+1)-fp(vv,n))%p*fp(vv-1,p-2)%p;
        ans=(ans+v)%p;
        ans=(ans+p)%p;
    }
    else
    {
        for(int i=m;i>=1;i--)
            ans=(ans+gao(n-1,i)-gao(n-i-1,i))*vv%p;
        ans=ans*k%p;
        ans=(ans+p)%p;
    }
    printf("%lld\n",ans);
}
int main()
{
    open("a");
    scanf("%d%d%d",&m,&k,&n);
    if(m<=200)
        solve1();
    else
        solve2();
    return 0;
}

转载于:https://www.cnblogs.com/ywwyww/p/8573364.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值