码迷,mamicode.com
首页 > 其他好文 > 详细

[SDOI2015]序列统计

时间:2019-02-16 15:40:41      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:min   clu   tchar   次方   自己   ||   include   方式   phi   

题目

这题神仙的一批

看到乘法显然并不是很好做,尝试将其转化为加法

乘法变加法最常见的方式就是取\(log\)

\[\prod_{i=1}^na_i\equiv x(mod\ P)\]

就变成了

\[\sum_{i=1}^n log(a_i)\equiv log(x)(mod\ P)\]

但是在模意义下取\(log\)显然不是很可行啊

其实只需要找到一个合理的底数就好了

所以我们只需要找到原根就好了

如果\(g\)\(P\)的原根,那么\(g^0,g^1,g^2....g^{\varphi(P)-1}\)\(\varphi(P)\)个数肯定两两不同,又因为\(\varphi(P)=P-1\),所以\(g\)的幂次方恰好能表示\(mod\ P\)意义下的所有整数

于是我们就把相乘转化成了指数相加

我们设一个多项式\(f(x)\)\(f(x)\)表示\(x\)这个指数出现的次数

我们让\(f\)自己卷一下

\[f^2(x)=\sum_{i=0}^xf(i)f(x-i)\]

发现如果我们暴力\(dp\)求一下两个指数相加形成的方案也是这个柿子

于是我们就可以用卷积来代替暴力的背包\(dp\)

答案就是\(f^n(x)\)

需要一个多项式快速幂

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100005
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
    char c=getchar();int x=0;while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const LL Mod=1004535809;
const LL g[2]={3,334845270};
int N,mod,T,n,len=1;
int rev[maxn],tax[maxn];
LL a[maxn],b[maxn],S[maxn],C[maxn];
inline LL quick(LL a,LL b) {LL S=1;while(b) {if(b&1ll) S=S*a%Mod;b>>=1;a=a*a%Mod;}return S;}
inline void NTT(LL *f,int o) {
    for(re int i=0;i<len;i++) if(i<rev[i]) std::swap(f[i],f[rev[i]]);
    for(re int i=2;i<=len;i<<=1) {
        int ln=i>>1;LL og1=quick(g[o],(Mod-1)/i);
        for(re int l=0;l<len;l+=i) {
            LL og=1,t;
            for(re int x=l;x<l+ln;x++) {
                t=(og*f[x+ln])%Mod;
                f[x+ln]=(f[x]-t+Mod)%Mod;
                f[x]=(f[x]+t)%Mod;
                og=(og*og1)%Mod;
            }
        }
    }
    if(!o) return;
    LL inv=quick(len,Mod-2);
    for(re int i=0;i<len;i++) f[i]=(f[i]*inv)%Mod;
}
inline void mul(LL *A,LL *B) {
    for(re int i=0;i<len;i++) C[i]=B[i];
    NTT(A,0),NTT(C,0);
    for(re int i=0;i<len;i++) A[i]=(A[i]*C[i])%Mod;
    NTT(A,1);
    for(re int i=mod-1;i<len;i++) A[i%(mod-1)]=(A[i%(mod-1)]+A[i])%Mod,A[i]=0;
}
inline int check(int x) {
    memset(tax,0,sizeof(tax));
    int now=1;
    for(re int i=0;i<mod-1;i++) {
        if(tax[now]) return 0;tax[now]=i;
        now=(now*x)%mod;
    }
    return 1;
}
inline void Quick(LL B) {
    S[0]=1;
    while(B) {
        if(B&1ll) mul(S,b);
        B>>=1ll;
        mul(b,b);
    }
}
int main()
{
    N=read(),mod=read(),T=read(),n=read();
    for(re int i=2;i<mod;i++) 
        if(check(i)) break;
    for(re int i=1;i<=n;i++) a[i]=read(),a[i]%=mod;
    for(re int i=1;i<=n;i++) 
        if(a[i]) b[tax[a[i]]]++;
    while(len<=mod+mod) len<<=1;
    for(re int i=0;i<len;i++) 
        rev[i]=(rev[i>>1]>>1)|((i&1)?len>>1:0);
    Quick(N);
    printf("%lld\n",S[tax[T]]);
    return 0;
}

[SDOI2015]序列统计

标签:min   clu   tchar   次方   自己   ||   include   方式   phi   

原文地址:https://www.cnblogs.com/asuldb/p/10387662.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!