「SDOI2015」序列统计

题目链接:Click here

Solution:

容易得到这样一个\(dp\),设\(f[i][j]\)表示已经选了\(i\)个数,乘积\(mod \,\,m\)后为\(j\)的方案
\[
f[2\times i][j]=\sum_{a\times b\equiv j\,\,(mod\,\, m)} f[i][a]\times f[i][b]
\]
考虑如何优化这个转移方程,注意到这道题的模数为\(1004535809\),这是一个很大的提示,考虑\(NTT\)

但是我们知道\(NTT\)处理的是卷积,而这里是指数相乘,该怎么转化呢?

乘法变加法,不由得让我们想到对数,模意义下的对数,又不由得让我们想到原根

考虑原根的定义,\(g\)是\(p\)的原根,则\(g^0 ,g^1\dots g^{p-2}\)在\(mod \,\,p\)下恰好取到\([1,p-1]\)的所有整数

原根也很好求,把\(\varphi(p)=\prod pr_i^{a_i}\),然后对于所有的\(pr_i\)都有\(g^{\varphi(p) \over pr_i} \not \equiv 1 \,\, ( mod \,\, p)\)

那么我们令\(j=\log_g j,a=\log_g a,b=\log_g b\),同时根据扩展欧拉定理,转移方程就变成了
\[
f[2\times i][j]=\sum_{a+b \equiv j \,\, (mod \,\, \varphi(m))}f[i][a]\times f[i][b]
\]
注意每次转移后\(f[i][j]+=f[i][j+\varphi(m)]\),我们就可以愉快的\(NTT\)啦!

等等,好像还不行,\(n=1e9\),仔细观察这个转移,发现可以直接多项式快速幂,那么再快速幂即可,时间复杂度\(O(m\log m \log n)\)

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e4+11;
const int mod=1004535809;
int n,m,S,X,len=1,tim;
int f[N],v[N],p[N],pr[N],lg[N];
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int qpow(int x,int y,int p){
    int re=1;
    while(y>0){
        if(y&1) re=re*x%p;
        y>>=1;x=x*x%p;
    }return re;
}
int GetRt(int x){
    int tot=0,phi=x-1;
    for(int i=2;i*i<=phi;i++)
        if(phi%i==0){
            pr[++tot]=i;
            while(phi%i==0) phi/=i;
        }
    if(phi>1) pr[++tot]=phi;
    phi=x-1;
    for(int i=2;i<=phi;i++){
        int flag=1;
        for(int j=1;j<=tot&&flag;j++)
            if(qpow(i,phi/pr[j],x)==1) flag=0;
        if(flag) return i;
    }
    return -1;
}
void NTT(int *a,int flag){
    for(int i=0;i<len;i++)
        if(i<p[i]) swap(a[i],a[p[i]]);
    for(int l=2;l<=len;l<<=1){
        int wn=qpow(3,(mod-1)/l,mod);
        if(flag==-1) wn=qpow(wn,mod-2,mod);
        for(int st=0;st<len;st+=l){
            int w=1;
            for(int u=st;u<st+(l>>1);u++,w=w*wn%mod){
                int x=a[u],y=w*a[u+(l>>1)]%mod;
                a[u]=(x+y)%mod;a[u+(l>>1)]=(x+mod-y)%mod;
            }
        }
    }
}
void Mul(int *A,int *B,int *C){
    static int a[N],b[N];
    for(int i=0;i<len;i++) a[i]=A[i];
    for(int i=0;i<len;i++) b[i]=B[i];
    NTT(a,1);NTT(b,1);
    for(int i=0;i<len;i++) a[i]=a[i]*b[i]%mod;
    NTT(a,-1);int inv=qpow(len,mod-2,mod);
    for(int i=0;i<len;i++) a[i]=a[i]*inv%mod;
    for(int i=0;i<m-1;i++) C[i]=(a[i]+a[i+m-1])%mod;
}
signed main(){
    n=read(),m=read(),X=read(),S=read();
    while(len<(m<<1)) len<<=1,++tim;
    for(int i=0;i<len;i++)
        p[i]=(p[i>>1]>>1)|((i&1)<<(tim-1));
    int g=GetRt(m);
    for(int i=0;i<m-1;i++) lg[qpow(g,i,m)]=i;
    for(int i=1;i<=S;i++){
        int x=read()%m;
        if(x) ++v[lg[x]];
    }
    f[lg[1]]=1;
    while(n){
        if(n&1) Mul(f,v,f);
        n>>=1;Mul(v,v,v);
    }
    printf("%lld\n",f[lg[X]]);
    return 0;
}

原文地址:https://www.cnblogs.com/NLDQY/p/12242664.html

时间: 2024-10-08 20:47:33

「SDOI2015」序列统计的相关文章

AC日记——「SDOI2017」序列计数 LibreOJ 2002

「SDOI2017」序列计数 思路: 矩阵快速幂: 代码: #include <bits/stdc++.h> using namespace std; #define mod 20170408 #define ll long long struct MatrixType { int n,m; ll ai[105][105]; void mem(int n_,int m_) { n=n_,m=m_; for(int i=0;i<=n;i++) for(int v=0;v<=m;v++

LibreOJ #2002. 「SDOI2017」序列计数

二次联通门 : LibreOJ #2002. 「SDOI2017」序列计数 /* LibreOJ #2002. 「SDOI2017」序列计数 线性筛 + 矩阵优化dp 先构造出全部情况的矩阵 用矩阵快速幂计算答案 再构造出全不是质数的矩阵 计算出答案 前一个答案减后一个答案即可 */ #include <cstdio> #include <iostream> #include <cstring> const int BUF = 12312312; char Buf[BU

「JSOI2014」序列维护

「JSOI2014」序列维护 传送门 其实这题就是luogu的模板线段树2,之所以要发题解就是因为被 \(\color{black}{\text{M}} \color{red}{\text{_sea}}\) 告知了一种比较NB的 \(\text{update}\) 的方式. 我们可以把修改操作统一化,视为 \(ax + b\) 的形式,然后我们按照原来的套路来维护两个标记,分别代表 \(a\) 和 \(b\) ,那么我们的更新就可以这么写: inline void f(int p, int at

bzoj3992【SDOI2015】序列统计

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MB Submit: 673  Solved: 327 [Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,能够生成一个长度为N的数列,数列中的每一个数都属于集合S. 小C用这个生成器生成了很多这种数列.可是小C有一个问题须要你的帮助:给定整数x,求全部能够生成出的,且满足数列

BZOJ 3992 【SDOI2015】 序列统计

题目链接:序列统计 我来复习板子了--这道题也是我写的第一发求原根啊? 求原根方法: 从小到大依次枚举原根.设当前枚举的原根为\(x\),模数为\(p\),\(p-1\)的质因数分别为\(p_1,p_2,\dots,p_m\),则只需检验\(x^{\frac{p}{p_i}}\equiv1 \pmod{p}\)是否成立即可.如果成立则\(x\)不是原根. 然后这道题朴素\(dp\)就不讲了.设\(m\)的原根为\(g\),那么把每个数表示成\(g^k\)的形式就可以乘法变加法了,就成为了\(NT

「LuoguP1430」 序列取数

题目描述 给定一个长为n的整数序列(n<=1000),由A和B轮流取数(A先取).每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取.所有数都被取走后,两人分别统计所取数的和作为各自的得分.假设A和B都足够聪明,都使自己得分尽量高,求A的最终得分. 输入输出格式 输入格式: 第一行,一个正整数T,表示有T组数据.(T<=100) 接着T行,每行第一个数为n,接着n个整数表示给定的序列. 输出格式: 输出T行,每行一个整数,表示A的得分 输入输出样例 输入样例#1: 复制 2 1

【BZOJ3992】【SDOI2015】序列统计

数论劲啊 原题: 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535

loj2051 「HNOI2016」序列

ref #include <algorithm> #include <iostream> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; int n, q, a[100005], l[100005], r[100005], sta[100005], din, blc, bel[100005], st[100005][19], mii[17], mlg[

loj#2002. 「SDOI2017」序列计数(dp 矩阵乘法)

题意 题目链接 Sol 质数的限制并没有什么卵用,直接容斥一下:答案 = 忽略质数总的方案 - 没有质数的方案 那么直接dp,设\(f[i][j]\)表示到第i个位置,当前和为j的方案数 \(f[i + 1][(j + k) \% p] += f[i][j]\) 矩乘优化一下. #include<bits/stdc++.h> #define LL long long using namespace std; const int MAXN = 2e7 + 10, mod = 20170408,