题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992

有转移次数、模M余数、方案数三个值,一看就是系数的地方放一个值、指数的地方放一个值、做卷积的次数表示一个值(应该是表示转移次数)。

可以余数和方案数都要求相乘,指数只能相加,怎么办?

然后看题解,原来可以用M的原根的幂来表示余数那个信息!因为原根的几次幂和%M剩余类可以一一对应(除了%M==0!!!),所以用原根的幂表示%M余几,两个余数相乘就变成原根的指数相加了!把该余数对应的原根的指数放在多项式指数的位置,就可以NTT啦!

原根是 1~P-1 次幂的值%P各不相同的,所以可以用 0次项~M-2次项 或者 1次项~M-1 次项来表示。

n的范围要求快速幂。但不是把点值拿出来之后对点值快速幂一番再用点值还原回系数,因为每次卷积那个多项式的长度都要翻倍,所以最后n个点的个数就不够了。

所以要快速幂中每次卷积了一下后把它翻倍的长度手动循环一番变回原长M。这样就行啦!

注意数据范围!!!求的那个 x 不能为0,而给出的元素可以为0!而原根的那些幂都不会为0!(仔细一想,只有0或M的倍数才会%M==0)考虑到那个 x 不会为0、而数列中放入一个0的话值就变成0了,所以给出0以后要认为没有那个元素!!!!!

快速幂时,ans的初值应该像1一样;也就是一个多项式卷积它之后还是该多项式本身。想一想,就是在0次项赋1、其他项赋0即可。

发现>(M<<1)的项的值一定是0;所以循环的时候可以直接减掉1个(M-1)而不用模什么的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=; const ll mod=;
int n,m,M,pn,s[N],zb[N],pri[N],len,r[N<<];
int a[N<<],ans[N<<];
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='') ret=(ret<<)+(ret<<)+ch-'',ch=getchar();
return fx?ret:-ret;
}
void upd(int &x,int md){x>=md?x-=md:;}
int pw(int x,int k,int md)
{int ret=;while(k){if(k&)ret=(ll)ret*x%md;x=(ll)x*x%md;k>>=;}return ret;}
int gtrt()
{
int k=M-,tot=;
for(int i=;i*i<=k;i++)
if(k%i==){pri[++tot]=i;while(k%i==)k/=i;}
if(k>)pri[++tot]=k;
for(int g=;;g++)
{
int i;
for(i=;i<=tot;i++)
if(pw(g,(M-)/pri[i],M)==)break;
if(i>tot)return g;
}
}
void ntt(int *a,bool fx)
{
for(int i=;i<len;i++)
if(i<r[i])swap(a[i],a[r[i]]);
for(int R=;R<=len;R<<=)
{
int m=R>>;
int Wn=pw(,(mod-)/R,mod);
fx?Wn=pw(Wn,mod-,mod):;
for(int i=;i<len;i+=R)
for(int j=,w=;j<m;j++,w=(ll)w*Wn%mod)
{
int tmp=(ll)w*a[i+m+j]%mod;
a[i+m+j]=a[i+j]+mod-tmp; upd(a[i+m+j],mod);
a[i+j]=a[i+j]+tmp; upd(a[i+j],mod);
}
}
if(!fx)return; int inv=pw(len,mod-,mod);
for(int i=;i<len;i++)a[i]=(ll)a[i]*inv%mod;
}
int main()
{
n=rdn(); M=rdn(); pn=rdn(); m=rdn();
for(int i=;i<=m;i++)s[i]=rdn();
int rt=gtrt();
for(int i=,k=rt;i<M;i++,k=k*rt%M) zb[k]=i;
len=;
for(;len<=M<<;len<<=);
for(int i=;i<len;i++)r[i]=(r[i>>]>>)+((i&)?len>>:); for(int i=;i<=m;i++)if(s[i])a[zb[s[i]]]=;////if
ans[]=;///
while(n)
{
ntt(a,);
if(n&)
{
ntt(ans,);
for(int i=;i<len;i++)ans[i]=(ll)ans[i]*a[i]%mod;
ntt(ans,);
for(int i=;i<M;i++)//pos which >(M<<1) surely have no value
ans[i]+=ans[i+M-],ans[i+M-]=,upd(ans[i],mod);
}
for(int i=;i<len;i++)a[i]=(ll)a[i]*a[i]%mod;
ntt(a,);
for(int i=;i<M;i++)
a[i]+=a[i+M-],a[i+M-]=,upd(a[i],mod);
n>>=;
}
printf("%d\n",ans[zb[pn]]);
return ;
}

bzoj 3992 [SDOI2015]序列统计——NTT(循环卷积&&快速幂)的更多相关文章

  1. bzoj 3992 [SDOI2015] 序列统计 —— NTT (循环卷积+快速幂)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3992 (学习NTT:https://riteme.github.io/blog/2016-8 ...

  2. BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1155  Solved: 532[Submit][Statu ...

  3. 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

    [BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...

  4. bzoj 3992: [SDOI2015]序列统计 NTT+原根

    今天开始学习丧心病狂的多项式qaq......    . code: #include <bits/stdc++.h> #define ll long long #define setIO ...

  5. BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)

    3992: [SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S ...

  6. BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Statu ...

  7. [BZOJ 3992][SDOI2015]序列统计

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 2275  Solved: 1090[Submit][Stat ...

  8. bzoj 3992: [SDOI2015]序列统计【原根+生成函数+NTT+快速幂】

    还是没有理解透原根--题目提示其实挺明显的,M是质数,然后1<=x<=M-1 这种计数就容易想到生成函数,但是生成函数是加法,而这里是乘法,所以要想办法变成加法 首先因为0和任何数乘都是0 ...

  9. BZOJ.3992.[SDOI2015]序列统计(DP NTT 原根)

    题目链接 \(Description\) 给定\(n,m,x\)和集合\(S\).求\(\prod_{i=1}^na_i\equiv x\ (mod\ m)\)的方案数.其中\(a_i\in S\). ...

随机推荐

  1. php token 生成

    php token的生成   接口特点汇总: 1.因为是非开放性的,所以所有的接口都是封闭的,只对公司内部的产品有效: 2.因为是非开放性的,所以OAuth那套协议是行不通的,因为没有中间用户的授权过 ...

  2. Linux挂载Windows共享目录

    在windows中设置共享目录并添加权限用户 把Window系统的文件共享挂载到linux centos 目录下的方法步骤: 1.先在windows下面共享需要挂载的目录. 2.确保linux与win ...

  3. SQL单行函数和多行函数

    单行函数和多行函数示意图: 单行函数分为五种类型:字符函数.数值函数.日期函数.转换函数.通用函数 单行函数: --大小写控制函数 select lower('Hello World') 转小写, u ...

  4. php数组函数-array_diff()

    array_diff()函数返回两个数组的差集数组.该数组包括了所有在被比较数组中,但是不在任何其他参数数组中的键值在返回数组中,键名保持不变. array_diff(array1,array2,ar ...

  5. Android电容屏(一)【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/7820492 关键词:Android  电容屏 tp  ITO 平台信息:内核:linu ...

  6. freemarker内建函数介绍

    Sequence的内置函数1.sequence?first 返回sequence的第一个值.2.sequence?last 返回sequence的最后一个值.3.sequence?reverse 将s ...

  7. 语义web基础知识学习

    最近找了一本书,<语义web技术基础>----Pascal Hitzler等编著的.因为最近在看关于自然语言处理的一些东西,就顺带翻了一下,做一点学习笔记,方便以后查看. 本书的作者 Pa ...

  8. /var/spool/clientmqueue 爆满问题

    当你使用简单的sendmail发邮件的时候,或者系统默认要发一些邮件(比如cron发的邮件)的时候,首先会把邮件拷贝到这个目录里,然后等待MTA(mail transfer agent) 来处理,MT ...

  9. 【P2325】王室联邦(树的遍历+贪心)

    在肖明 #神#的推荐下,我尝试了这个题,一开始想的是暴力枚举所有的点,然后bfs层数,试着和肖明 #神#说了这种方法之后, #神#轻蔑的一笑,说这不就是一个贪心么,你只需要先建树,然后从底下向上遍历, ...

  10. matlab学习笔记,图像分块

    clc; clear all; close all; I = imread('E:\matlab\files-images\tomsen512.jpg'); rs = size(I, 1);% 行数c ...