• 【题解】【原创题目】第〇类循环数•行


    【题解】【原创题目】第〇类循环数·行

    传送门:第〇类循环数·行

    【题目描述】

    给出 (n,m,l,k,x),求 (frac{1}{nml}sumlimits_{a=1}^{n}sumlimits_{b=1}^{m}sumlimits_{c=1}^{l}f(k,(ax^2+bx+c)mod k) pmod{1004535809}),其中 (f(i,j)) 定义如下:

    [egin{cases}i^2+i&iin[1,infty],j=0\frac{f(i-1,j-1)}{ij}+frac{f(i-1,j-1)+[j<i-1]f(i-1,j)}{i}+frac{f(i-1,j-1)}{j}+f(i!-!1,j!-!1)+[j! eq!i!-!1]f(i!-!1,j)&iin[2,infty],jin[1,i!-!1]end{cases} ]

    【分析】

    【Subtask #1】

    抄题目描述总会吧。

    本测试包轻微卡常,需要提前把 (f) 全部预处理出来。

    复杂度 (O(k^2+n^3T)),期望得分:(1pts)

    【Code #1】
    in(T),inv[1]=1;
    for(Re i=2;i<=M-3;++i)inv[i]=(LL)inv[P%i]*(P-P/i)%P;
    for(Re i=1;i<=M-3;++i){
        f[i][0]=i*i+i;
        for(Re j=1;j<=i-1;++j)
            f[i][j]=((j?(
                (LL)inv[i]*inv[j]%P*f[i-1][j-1]%P+
                (LL)inv[i]*f[i-1][j-1]%P+
                (LL)inv[j]*f[i-1][j-1]%P+
                f[i-1][j-1]
            )%P:0)+
            (j<i-1?((LL)inv[i]*f[i-1][j]%P+f[i-1][j])%P:0))%P;
    }
    while(T--){
        in(n),in(m),in(l),in(K),in(x),x%=K,x2=(LL)x*x%K,ans=0;
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=m;++j)
                for(Re k=1;k<=l;++k)
                    (ans+=f[K][((LL)i*x2%K+(LL)j*x%K+k)%K])%=P;
        printf("%lld
    ",(LL)ans*Inv(n)%P*Inv(m)%P*Inv(l)%P);
    }
    

    【Subtask #2】

    存在特殊性质 (k|x),说明 (q)(a,b) 无关,即求 (frac{1}{l}sum_{c=1}^{l}f(k,cmod k)),发现暴力枚举有很多重复计算,让 (l)(k) 取个模即可降到 (O(k))

    本测试包轻微卡常。

    复杂度 (O(k^2+kT)),期望得分:(1+9=10pts)

    【Code #2】
    while(T--){
        in(n),in(m),in(l),in(K),in(x),x%=K,x2=(LL)x*x%K,ans=0;
        Re tmp=0;
        for(Re k=0;k<K;++k)(tmp+=f[K][k])%=P;
        (ans+=(LL)l/K%P*tmp%P)%=P;
        for(Re k=1;k<=(l%K);++k)(ans+=f[K][k])%=P;
        printf("%lld
    ",(LL)ans*Inv(l)%P);
    }
    

    【Subtask #3】

    用类似 ( ext{Subtask 2}) 的降级做法,依次处理两个东西:(g_1(a)=sum_{i=1}^{l}f_{k}(a+i),) (g_2(a)=sum_{i=1}^{m}g_1(a+ix)),最后答案为 (sum_{i=1}^{n}g_{2}(ix^2))

    本做法较卡常,需要把加法取模优化掉。

    复杂度 (O(k^2+k^2T)),期望得分:(1+9+11=21pts)

    【Code #3】
    inline void mo(Re &x,Re mod=P){x=x>=mod?x-mod:x;}
    inline int Mo(Re x,Re mod=P){return x>=mod?x-mod:x;}
    int main(){
        while(T--){
            in(n),in(m),in(l),in(K),in(x),x%=K,x2=(LL)x*x%K,ans=0;
            for(Re a=0;a<K;++a){
                Re tmp=0;
                if(l>=K)for(Re k=0,Tmp=a;k<K;++k)mo(tmp+=f[K][Tmp]),mo(++Tmp,K);
                g1[a]=(LL)l/K%P*tmp%P;
                for(Re k=1,Tmp=Mo(a+1,K),ed=l%K;k<=ed;++k)mo(g1[a]+=f[K][Tmp]),mo(++Tmp,K);
            }
            for(Re a=0;a<K;++a){
                Re tmp=0;
                if(m>=K)for(Re k=0,Tmp=a;k<K;++k)mo(tmp+=g1[Tmp]),mo(Tmp+=x,K);
                g2[a]=(LL)m/K%P*tmp%P;
                for(Re k=1,Tmp=Mo(a+x,K),ed=m%K;k<=ed;++k)mo(g2[a]+=g1[Tmp]),mo(Tmp+=x,K);
            }
            Re tmp=0;
            if(n>=K)for(Re k=0,Tmp=0;k<K;++k)mo(tmp+=g2[Tmp]),mo(Tmp+=x2,K);
            ans=(LL)n/K%P*tmp%P;
            for(Re k=1,Tmp=x2,ed=n%K;k<=ed;++k)mo(ans+=g2[Tmp]),mo(Tmp+=x2,K);
            printf("%lld
    ",(LL)ans*Inv(n)%P*Inv(m)%P*Inv(l)%P);
        }
    }
    

    【Subtask #4】

    观察 (f) 递推式,首先通分合并同类项:

    [f(i,j)=frac{(i+1)(j+1)}{ij}f(i-1,j-1)+[j eq i-1]frac{(i+1)}{i}f(i-1,j) ]

    两边同时除以 ((i+1)(j+1))

    [frac{f(i,j)}{(i+1)(j+1)}=frac{f(i-1,j-1)}{ij}+[j eq i-1]frac{f(i-1,j)}{i(j+1)} ]

    发现是组合数的杨辉三角递推式。设 (g(i,j)=frac{f(i,j)}{(i+1)(j+1)}),则有 (g(i,j)=g(i-1,j-1)+[j eq i-1]g(i-1,j))

    注意此处的细节:通常组合数递推为 (dbinom{n}{m}=dbinom{n-1}{m-1}+dbinom{n-1}{m}),当 (m=n)(dbinom{n-1}{m}) 才为 (0),但 (g(i-1,j)) 前面的系数为 ([j eq i-1]),因此 (g(i,j)) 应该等于(dbinom{i}{j+1}) 而不是 (dbinom{i}{j}) ,否则就与递推式不相符。

    得出 (f(i,j)=(i+1)(j+1)dbinom{i}{j+1}),这个东西算出来的 (f(i,0)) 和前面定义是符合的。

    注意到 (n) 比较小,预处理出组合数后直接沿用 ( ext{Subtask 3}) 的做法即可。

    复杂度 (O(min(n,k)kT)),期望得分:(1+9+11+19=40pts)

    【Code #4】

    代码略。

    【Subtask #5】

    推出 (f) 通项式后直接沿用 ( ext{Subtask 2}) 的做法。

    此做法良心出题人没有卡常。

    复杂度 (O(kT)),期望得分:(9+18=27pts),结合前面算法可得 (58pts)

    【Code #5】

    代码略。

    【Subtask #6】

    前置知识:单位根反演。【常见公式汇总】

    按照套路,枚举 (f) 的参数并交换和式:

    [frac{1}{nml}sum_{d=0}^{k-1}f(k,d)sumlimits_{a=1}^{n}sumlimits_{b=1}^{m}sumlimits_{c=1}^{l}[(ax^2+bx+c)mod k=d] ]

    ([(ax^2+bx+c)mod k=d]) 可化为 ([k|(ax^2+bx+c-d)]),套用单位根反演的柿子,得到:

    [frac{1}{nml}sum_{d=0}^{k-1}f(k,d)sumlimits_{a=1}^{n}sumlimits_{b=1}^{m}sumlimits_{c=1}^{l}frac{1}{k}sum_{i=0}^{k-1}w_{k}^{i(ax^2+bx+c-d)} ]

    提一个 (w_{k}^{-id}) 出来,并交换和式:

    [frac{1}{nmlk}sum_{i=0}^{k-1}sum_{d=0}^{k-1}f(k,d)w_{k}^{-id}sumlimits_{a=1}^{n}sumlimits_{b=1}^{m}sumlimits_{c=1}^{l}w_{k}^{i(ax^2+bx+c)} ]

    右边那一坨显然可以简化为三个等比数列和的乘积,记一个 (calc) 函数:

    [calc(w)=sumlimits_{a=1}^{n}w^{ax^2}sumlimits_{b=1}^{m}w^{bx}sumlimits_{c=1}^{l}w^{c}=frac{w^{x^2}(1-(w^{x^2})^{n})}{1-w^{x^2}} imesfrac{w^{x}(1-(w^{x})^{m})}{1-w^{x}} imesfrac{w^{}(1-w^{l})}{1-w^{}} ]

    用组合数吸收公式去掉 (f) 柿子里的 ((j+1)),即 (f(i,j)=(i+1)(j+1)dbinom{i}{j+1}=i(i+1)dbinom{i-1}{j}),然后代进去用二项式定理合并:

    [frac{1}{nmlk}sum_{i=0}^{k-1}sum_{d=0}^{k-1}k(k+1)dbinom{k-1}{d}w_{k}^{-id}calc(w_{k}^{i})\ =frac{k+1}{nml}sum_{i=0}^{k-1}calc(w_{k}^{i})sum_{d=0}^{k-1}dbinom{k-1}{d}w_{k}^{-id}\ =frac{k+1}{nml}sum_{i=0}^{k-1}calc(w_{k}^{i})(w_{k}^{-i}+1)^{k-1} ]

    本测试包不卡常,可以直接暴算。

    复杂度 (O(Tklog n))),期望得分:(91pts)

    【Code #6】
    inline int calc(Re x,Re n){//注意等比数列求和要特判公比等于1的情况 
        return x==1?n:(LL)x*(1-mi(x,n)+P)%P*Inv((1-x+P)%P)%P;
    }
    inline int calc(Re w){
        return (LL)calc(mi(w,x2),n)*calc(mi(w,x),m)%P*calc(w,l)%P;
    }
    int main(){
        in(T);
        while(T--){
            in(n),in(m),in(l),in(K),in(x),x%=K,x2=(LL)x*x%K,ans=0;
            omega[0]=1,omega[1]=mi(G,(P-1)/K);
            for(Re i=2;i<=K;++i)omega[i]=(LL)omega[i-1]*omega[1]%P;
            for(Re i=0;i<=K-1;++i)
                (ans+=(LL)mi(omega[K-i]+1,K-1)*calc(omega[i])%P)%=P;
            ans=(LL)ans*(K+1)%P;
            printf("%lld
    ",(LL)ans*Inv(n)%P*Inv(m)%P*Inv(l)%P);
        }
    }
    

    【Subtask #7】

    最后两个测试点需要大力卡常。

    大常数主要来源于快速幂,枚举一个 (i) 要调用 (9) 次,极限数据跑了约 (5.8s)(洛谷上要快 (0.6s))。

    实际上其中有 (5) 次都在算单位根 (w_{k}^{1}) 的若干次幂,由于 (w_{k}^{i}=w_{k}^{imod k}),只要预处理出 (0sim k-1) 次幂就能 (O(1)) 查询了。
    然后是 (3) 次计算逆元,同样可以预处理,具体做法见 乘法逆元 (2) ( ext{[P5431]})

    其实单位根还可以放到询问前面预处理,对于不同的 (k) 根据 (w_{k}^{a}=w_{kn}^{an}) 这一性质直接得到。
    但瓶颈不在于此,常数影响不大。

    复杂度 (O(Tklog k))),期望得分:(100pts)

    【Code #7】
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LD double
    #define LL long long
    #define Re register int
    using namespace std;
    const int N=1e9+3,M=1048576+3,P=1004535809,G=3;
    int n,m,l,K,T,x2,ans,Sm[M],invo[M],omega[M];LL x;
    template<typename T>inline void in(T &x){
        int f=0;x=0;char ch=getchar();
        while(ch<'0'||ch>'9')f|=ch=='-',ch=getchar();
        while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=f?-x:x;
    }
    inline int mi(Re x,Re k){
        Re s=1;
        while(k){
            if(k&1)s=(LL)s*x%P;
            x=(LL)x*x%P,k>>=1;
        }
        return s;
    }
    inline int Inv(Re x){return mi(x,P-2);}
    inline void mo(Re &x,Re mod=P){x=x>=mod?x-mod:x;}
    inline int Mo(Re x,Re mod=P){return x>=mod?x-mod:x;}
    inline int calc(Re i,Re n){//注意等比数列求和要特判公比等于1的情况
        return (omega[i]==1?n:(LL)omega[i]*(omega[(LL)i*n%K]-1+P)%P*invo[i]%P);
    }
    int main(){
        in(T);
        while(T--){
            in(n),in(m),in(l),in(K),in(x),x%=K,x2=(LL)x*x%K,ans=0;
            omega[0]=Sm[0]=1,Sm[1]=(omega[1]=mi(G,(P-1)/K))-1;
            for(Re i=2;i<=K;++i)omega[i]=(LL)omega[i-1]*omega[1]%P,Sm[i]=(LL)Sm[i-1]*(omega[i]-1)%P;
            Re tmp=Inv(Sm[K-1]);
            for(Re i=K-1;i>=1;--i)invo[i]=(LL)tmp*Sm[i-1]%P,tmp=(LL)tmp*(omega[i]-1)%P;
            for(Re i=0,mp1=0,mp2=0;i<K;++i)
                mo(ans+=(LL)mi(omega[K-i]+1,K-1)*calc(mp1,n)%P*calc(mp2,m)%P*calc(i,l)%P),mo(mp1+=x2,K),mo(mp2+=x,K);
            ans=(LL)ans*(K+1)%P;
            printf("%lld
    ",(LL)ans*Inv(n)%P*Inv(m)%P*Inv(l)%P);
        }
    }
    

    【题外话—命题报告】

    出题人:辰星凌

    验题人:鏡音リン
    (感谢神仙 ( ext{karry}) 解惑,( ext{ezoixx130}) 划水验题)

    (7)(28) 晚,翻看自己整理的数学知识点汇总时,发现单位根反演这个东西题不多,就想搞一个出来玩玩儿。但数学题并不好出,直接放柿子的话会被喷惨。但我又比较菜,斗不出组合意义,顿时感觉希望渺茫。

    抱着试一试的心态,随便选了个方向:求 (sum_{i,j}f(imod j)),其中 (f) 先待定成普通多项式和下降幂多项式两种情况。推推推,最后弄出来一长串柿子,用了各种各样的技巧,结果最后还是 (O(n^2)) !甚至常数还更大了!

    人傻了....

    后来调整数据范围,另外搞了个柿子,并斗出一个简单的 (f) 函数,消去了很多东西,似乎能出成一道题了。

    但这还不够,因为只是给出了一个柿子让做题者去推,这样是没有意义的。而且单反就那两种变化,由于白兔之舞的存在,似乎无论怎么出都会感觉很套路,多半会被狂 ( ext{D})

    那就....加上构造!

    (f) 函数里的组合数拆开,弄出一个递推式,在题面上加点迷惑元素,让出题者去尝试构造 (f),并在边界处设置一个坑人的细节问题。
    这样看起来似乎就不那么板了~(≥▽≤)/~
    其实还是很套路,只要对组合数够熟悉就能一眼秒)。

    另外,关于用 (f) 递推式求通项表示式,一开始是想用生成函数暴力解的,但貌似要求导积分,小蒟蒻对这一类的东西不太熟悉,如果有直接硬推成功解出来的巨佬,请接受蒟蒻真诚的膜拜stO

    emm....从巨佬那里得知 (f) 可以在 ( ext{oeis}) 上找到,自闭....

    还是不加入到公开赛了,免得被骂搬原式。

    ( ext{updata:})( ext{jklover}) 巨佬提醒,( ext{Subtask 3,4}) 里的做法是可以直接用 ( ext{DFT}) 优化的,总复杂度也为 (O(nlog n)),虽然常数大一些,但实际运行效果只比前面的无常数单 (log) 做法慢 (0.3s),过于柯啪.....
    懒得写新 std 和重新造 Subtask 卡常了,就酱吧。

  • 相关阅读:
    五种I/O模型
    Python socket服务
    Python 协程
    python openpyxl 简单使用
    python 文件夹压缩
    Python 多进程
    MySQL 自定义函数
    python 队列
    python 多线程、线程锁、事件
    python paramiko模块:远程连接服务器
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/13427863.html
Copyright © 2020-2023  润新知