[BZOJ3328]PYXFIB/[JZOJ5150]却不悔付此华年

15 篇文章 0 订阅
7 篇文章 0 订阅

题目大意

给定 n,K,P ,求

i=0nk(niK)fibiK

P 取模的结果。
其中fibi是斐波那契数列的第 i 项。
一个测试点T组数据。

其中 1n1018,1k2×104,1p109,1T20
保证 p1(modK)


题目分析

好一道脑洞题。
题目相当于求

i=0n(ni)fibi[i mod K=0]

把斐波那契数列写成矩阵形式,令
A=(1110)

fibi=Ai11 ,题目变成求
i=0n(ni)Ai[i mod K=0]

先不考虑这个 [i mod K=0] ,然后发现这个和二项式定理很像
i=0n(ni)xi=(x+1)n

因为单位矩阵 I 满足可交换性,于是我们有
i=0n(ni)Ai=(A+I)n

现在考虑怎么解决 [i mod K=0] ,考虑构造一个函数 F(x) 乘在 Ai 后面,使其指数为 i 且可以等效代替[i mod K=0]
怎么构造呢?考虑使用单位根的性质来构造。
g 为模p意义下的原根,设 ω=gP1k 根据单位根的求和引理,我们有:
j=0K1ωij=0,K,i≢0(modK)i0(modK)

这个怎么证明?你等比数列求和一下,把 ω g 表示一下就好了。
于是答案等于
1Ki=0n(ni)Aij=0K1ωij

考虑把 j 提出去
1Kj=0K1i=0n(ni)Aiωij=1Kj=0K1(ωijA+I)n

枚举 j ,然后后面的部分可以直接矩阵快速幂,然后就做完了ヽ(≧∀≦)ノ
原根暴力找(一般比较小),判断的话只用检查P1除以 P1 的质因子的次幂。
时间复杂度 O(log2P+Klogn)


代码实现

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long LL;

const int L=31622;

int pri[L+5],p[L+5],a[L+5];
bool mark[L+5];
int T,K,P,g,cnt;
LL n;

struct matrix
{
    int num[2][2];
    int r,c;

    inline matrix operator*(matrix const mat)const
    {
        matrix ret;ret.r=r,ret.c=mat.c,memset(ret.num,0,sizeof ret.num);
        for (int i=0;i<ret.r;++i)
            for (int k=0;k<c;++k)
                if (num[i][k])
                    for (int j=0;j<ret.c;++j)
                        (ret.num[i][j]+=1ll*num[i][k]*mat.num[k][j]%P)%=P;
        return ret;
    }

    inline matrix operator+(matrix const mat)const
    {
        matrix ret;ret.r=r,ret.c=c;
        for (int i=0;i<ret.r;++i)
            for (int j=0;j<ret.c;++j)
                ret.num[i][j]=(num[i][j]+mat.num[i][j])%P;
        return ret;
    }

    inline matrix operator*(int const k)const
    {
        matrix ret;ret.r=r,ret.c=c;
        for (int i=0;i<ret.r;++i)
            for (int j=0;j<ret.c;++j)
                ret.num[i][j]=1ll*num[i][j]*k%P;
        return ret;
    }
}A,I,res;

inline matrix operator*=(matrix &x,matrix y){return x=x*y;}
inline matrix operator+=(matrix &x,matrix y){return x=x+y;}
inline matrix operator*=(matrix &x,int y){return x=x*y;}

inline matrix operator^(matrix x,LL y)
{
    matrix ret=I;
    for (;y;y>>=1,x*=x) if (y&1) ret*=x;
    return ret;
}

int quick_power(int x,int y)
{
    int ret=1;
    for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;
    return ret;
}

void pre()
{
    mark[1]=1;
    for (int i=2;i<=L;++i)
    {
        if (!mark[i]) pri[++pri[0]]=i;
        for (int j=1;j<=pri[0];++j)
        {
            if (1ll*i*pri[j]>L) break;
            mark[i*pri[j]]=1;
            if (!(i%pri[j])) break;
        }
    }
    A.r=A.c=2,A.num[0][0]=A.num[0][1]=A.num[1][0]=1;
    I.r=I.c=2,I.num[0][0]=I.num[1][1]=1;
}

void calc()
{
    cnt=0;
    int x=P-1;
    for (int i=1;i<=pri[0]&&1ll*pri[i]*pri[i]<=P-1&&x>1;++i)
        if (!(x%pri[i]))
        {
            p[++cnt]=pri[i],a[cnt]=0;
            for (;!(x%pri[i]);x/=pri[i]) ++a[cnt];
        }
    if (x>1) p[++cnt]=x,a[cnt]=1;
    g=0;
    for (int i=2;i<P&&!g;++i)
    {
        bool judge=1;
        for (int j=1;j<=cnt&&judge;++j) judge&=quick_power(i,(P-1)/p[j])!=1;
        if (judge) g=i;
    }
    int w=quick_power(g,(P-1)/K);
    res.r=res.c=2,memset(res.num,0,sizeof res.num);
    for (int j=0;j<K;++j) res+=((A*quick_power(w,j)+I)^n);
    res*=quick_power(K,P-2);
}

int main()
{
    freopen("you.in","r",stdin),freopen("you.out","w",stdout);
    for (pre(),scanf("%d",&T);T--;) scanf("%lld%d%d",&n,&K,&P),calc(),printf("%d\n",res.num[0][0]);
    fclose(stdin),fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值