题目大意

  求多项式 \(\prod_{i=1}^n(x+i)\) 的系数在模 \(p\) 意义下的分布,对 \(998244353\) 取模。

  \(p\) 为质数。

  \(n\leq {10}^{18},p\leq 250000\)

题解

  我们只计算 \([1,p-1]\) 的分布,最后再算出 \(0\) 的出现次数。

  记 \(n1=\lfloor\frac{n}{p}\rfloor,n2=n\bmod p\)。若 \(n\bmod p=p-1\),则 \(n1=\lfloor\frac{n}{p}\rfloor+1,n2=0\)。

\[\begin{align}
&\prod_{i=0}^n(x+i)\\
=&x^{n1}\prod_{i=1}^{p-1}(x+i)^{n1}\prod_{i=1}^{n2}(x+i)
\end{align}
\]


  第一部分我们直接忽略。


  对于第二部分,\(\prod_{i=1}^{p-1}(x+i)=x^{p-1}-1\)(因为左右两边的根都是 \(1,2,\ldots,p-1\))。那么 \(x^i(p-1)\) 项的系数就是 \(\binom{n1}{i}{(-1)}^{n1-i}\)。

  因为系数的模数为 \(p\),所以可以用 lucas 定理。

  \(\binom{x}{y}\equiv\prod_i\binom{x_i}{y_i}\pmod p\)

  假设 \(n1=(a_1a_2\ldots a_m)_p\)。

  对每一位构造一个多项式 \(A(x)\),\(a_i\) 表示有多少种方案满足这一位的贡献是 \(i\)。

  那么可以像数位DP一样,枚举 \(i\) 的前多少位和 \(n1\) 是相同的,然后求出后面的方案数。

  因为每一位都不能超过 \(a_i\),所以直接把每一位对应的多项式乘在一起就好了。

  这里的卷积稍微有点不同,\(a_i=\sum_{j}\sum_{k}[jk\bmod p=i]b_jc_k\),只需要把下标取离散对数就好了。

  不要忘记处理 \({(-1)}^{n1-i}\)。

  复杂度为 \(O(p\log_pn\log p)=O(p\log n)\)。


  对于第三部分,可以用倍增+任意模数FFT求出整个多项式。

  大概就是记 \(F(n,x)=\prod_{i=1}^n(x+i)\),那么 \(F(2n+1,x)=F(2n,x)(x+2n+1)\),\(F(2n,x)=F(n,x)F(n,x+n)\)。

  记 \(F(n,x)=\sum_ia_ix^i\),那么 \(F(n,x+n)=\sum_ia_i(x+n)^i=\sum_ia_i\sum_{j\leq i}x^jn^{i-j}\binom{i}{j}=\sum_{i}x_i\sum_{j\geq i}a_jn^{j-i}\binom{j}{i}\),直接卷积即可。

  复杂度为 \(O(p\log p)\)。


  由于第二部分的多项式形如 \(\sum_ia_ix^{i(p-1)}\),而第三部分的次数 \(<p-1\),所以两部分的乘积不互相干扰,可以分开求出后再合并到一起。

  总时间复杂度为 \(O(p(\log n+\log p))\)。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const ll p1=998244353;
const int N=530000;
const db pi=acos(-1);
ll fp(ll a,ll b,ll p)
{
ll s=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
s=s*a%p;
return s;
}
ll n,p,g;
ll n1,n2;
ll e1[N],e2[N];
namespace fft
{
struct cp
{
db x,y;
cp(db a=0,db b=0):x(a),y(b){}
};
cp operator +(cp a,cp b){return cp(a.x+b.x,a.y+b.y);}
cp operator -(cp a,cp b){return cp(a.x-b.x,a.y-b.y);}
cp operator *(cp a,cp b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
cp operator /(cp a,int b){return cp(a.x/b,a.y/b);}
cp muli(cp a){return cp(-a.y,a.x);}
cp divi(cp a){return cp(a.y,-a.x);}
cp conj(cp a){return cp(a.x,-a.y);}
const int W=524288;
cp w[N];
int rev[N];
void init()
{
for(int i=0;i<W/2;i++)
w[i]=cp(cos(2*pi/W*i),sin(2*pi/W*i));
}
void fft(cp *a,int n,int t)
{
for(int i=1;i<n;i++)
{
rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);
if(rev[i]>i)
swap(a[i],a[rev[i]]);
}
for(int i=2;i<=n;i<<=1)
for(int j=0;j<n;j+=i)
for(int k=0;k<i/2;k++)
{
cp u=a[j+k];
cp v=a[j+k+i/2]*w[W/i*k];
a[j+k]=u+v;
a[j+k+i/2]=u-v;
}
if(t==-1)
{
reverse(a+1,a+n);
for(int i=0;i<n;i++)
a[i]=a[i]/n;
}
}
void fft(db *a,db *b,cp *c,cp *d,int n)
{
static cp a1[N],a2[N];
for(int i=0;i<n;i++)
a1[i]=cp(a[i],b[i]);
fft(a1,n,1);
for(int i=0;i<n;i++)
a2[i]=conj(a1[i]);
reverse(a2+1,a2+n);
for(int i=0;i<n;i++)
{
c[i]=(a1[i]+a2[i])/2;
d[i]=divi(a1[i]-a2[i])/2;
}
}
void ifft(db *a,db *b,cp *c,cp *d,int n)
{
static cp a1[N];
for(int i=0;i<n;i++)
a1[i]=c[i]+muli(d[i]);
fft(a1,n,-1);
for(int i=0;i<n;i++)
{
a[i]=a1[i].x;
b[i]=a1[i].y;
}
}
void mul(ll *a,ll *b,ll *c,int n,int m,int l,ll p)
{
const static int M=1<<16;
static db a1[N],a2[N],b1[N],b2[N],c1[N],c2[N],d1[N],d2[N];
static cp a3[N],a4[N],b3[N],b4[N],c3[N],c4[N],d3[N],d4[N];
int k=1;
while(k<=n+m)
k<<=1;
for(int i=0;i<k;i++)
a1[i]=a2[i]=b1[i]=b2[i]=0;
for(int i=0;i<=n;i++)
{
a[i]=(a[i]+p)%p;
a1[i]=a[i]/M;
a2[i]=a[i]%M;
}
for(int i=0;i<=m;i++)
{
b[i]=(b[i]+p)%p;
b1[i]=b[i]/M;
b2[i]=b[i]%M;
}
fft(a1,a2,a3,a4,k);
fft(b1,b2,b3,b4,k);
for(int i=0;i<k;i++)
{
c3[i]=a3[i]*b3[i];
c4[i]=a3[i]*b4[i];
d3[i]=a4[i]*b3[i];
d4[i]=a4[i]*b4[i];
}
ifft(c1,c2,c3,c4,k);
ifft(d1,d2,d3,d4,k);
for(int i=0;i<=l;i++)
c[i]=((ll)(c1[i]+0.5)%p*M%p*M%p+(ll)(c2[i]+0.5)%p*M%p+(ll)(d1[i]+0.5)%p*M%p+(ll)(d2[i]+0.5)%p)%p;
}
void mul2(ll *a,ll *b,ll *c,int n,ll p)
{
static ll a1[N],a2[N],a3[N];
for(int i=1;i<n;i++)
{
a1[e2[i]]=a[i];
a2[e2[i]]=b[i];
}
mul(a1,a2,a3,n-2,n-2,2*n-4,p);
for(int i=1;i<n;i++)
c[i]=0;
for(int i=0;i<=2*n-4;i++)
c[e1[i%(n-1)]]=(c[e1[i%(n-1)]]+a3[i])%p;
}
}
ll inv[N],fac[N],ifac[N];
void init()
{
inv[1]=fac[0]=fac[1]=ifac[0]=ifac[1]=1;
for(int i=2;i<p;i++)
{
inv[i]=(-p/i*inv[p%i]%p+p)%p;
fac[i]=fac[i-1]*i%p;
ifac[i]=ifac[i-1]*inv[i]%p;
}
}
ll binom(int x,int y)
{
return x>=y&&y>=0?fac[x]*ifac[y]%p*ifac[x-y]%p:0;
}
int check()
{
ll s=1;
for(int i=1;i<=p;i++)
{
s=s*g%p;
if(s==1)
{
if(i==p-1)
return 1;
return 0;
}
}
return 0;
}
void getg()
{
for(g=1;;g++)
if(check())
break;
e1[0]=1;
for(int i=1;i<p-1;i++)
e1[i]=e1[i-1]*g%p;
for(int i=0;i<p-1;i++)
e2[e1[i]]=i;
}
namespace gao1
{
ll ans[N];
ll s[N];
void solve(int n)
{
if(!n)
{
s[0]=1;
return;
}
if(n&1)
{
solve(n-1);
for(int i=n-1;i>=0;i--)
{
s[i+1]=(s[i+1]+s[i])%p;
s[i]=s[i]*n%p;
}
}
else
{
solve(n>>1);
static ll a1[N],a2[N],a3[N];
for(int i=0;i<=n>>1;i++)
{
a1[i]=fp((n>>1),i,p)*ifac[i]%p;
a2[i]=s[i]*fac[i]%p;
}
reverse(a2,a2+(n>>1)+1);
fft::mul(a1,a2,a3,n>>1,n>>1,n>>1,p);
reverse(a3,a3+(n>>1)+1);
for(int i=0;i<=n>>1;i++)
a3[i]=a3[i]*ifac[i]%p;
fft::mul(s,a3,s,n>>1,n>>1,n,p);
}
}
void gao()
{
solve(n2);
for(int i=0;i<=n2;i++)
if(s[i])
ans[s[i]]++;
}
}
namespace gao2
{
ll ans[N];
int a[N],t;
ll s[N];
int v[N];
void gao()
{
static ll a1[N],a2[N];
for(ll _=n1;_;)
{
a[++t]=_%p;
_/=p;
}
ans[1]=1;
for(int j=1;j<=t;j++)
{
v[j]=(p==2&&j>1?1:-1);
for(int i=0;i<p;i++)
a1[i]=0;
for(int i=0;i<=a[j];i++)
a1[(binom(a[j],i)*(i&1?v[j]:1)%p+p)%p]++;
fft::mul2(ans,a1,a2,p,p1);
for(int i=1;i<p;i++)
ans[i]=a2[i];
}
}
}
ll ans[N];
int main()
{
open("b");
fft::init();
scanf("%lld%lld",&n,&p);
init();
getg();
n1=n/p,n2=n%p;
if(n2==p-1)
{
n1++;
n2=0;
}
gao1::gao();
gao2::gao();
fft::mul2(gao1::ans,gao2::ans,ans,p,p1);
ans[0]=(n+1)%p1;
for(int i=1;i<p;i++)
ans[0]=(ans[0]-ans[i])%p1;
for(int i=0;i<p;i++)
{
ans[i]=(ans[i]+p1)%p1;
printf("%lld\n",ans[i]);
}
return 0;
}

【集训队作业2018】取名字太难了 任意模数FFT的更多相关文章

  1. UOJ #449. 【集训队作业2018】喂鸽子

    UOJ #449. [集训队作业2018]喂鸽子 小Z是养鸽子的人.一天,小Z给鸽子们喂玉米吃.一共有n只鸽子,小Z每秒会等概率选择一只鸽子并给他一粒玉米.一只鸽子饱了当且仅当它吃了的玉米粒数量\(≥ ...

  2. [UOJ422][集训队作业2018]小Z的礼物——轮廓线DP+min-max容斥

    题目链接: [集训队作业2018]小Z的礼物 题目要求的就是最后一个喜欢的物品的期望得到时间. 根据$min-max$容斥可以知道$E(max(S))=\sum\limits_{T\subseteq ...

  3. 【UOJ#450】【集训队作业2018】复读机(生成函数,单位根反演)

    [UOJ#450][集训队作业2018]复读机(生成函数,单位根反演) 题面 UOJ 题解 似乎是\(\mbox{Anson}\)爷的题. \(d=1\)的时候,随便怎么都行,答案就是\(k^n\). ...

  4. 【UOJ#422】【集训队作业2018】小Z的礼物(min-max容斥,轮廓线dp)

    [UOJ#422][集训队作业2018]小Z的礼物(min-max容斥,轮廓线dp) 题面 UOJ 题解 毒瘤xzy,怎么能搬这种题当做WC模拟题QwQ 一开始开错题了,根本就不会做. 后来发现是每次 ...

  5. UOJ#418. 【集训队作业2018】三角形

    #418. [集训队作业2018]三角形 和三角形没有关系 只要知道儿子放置的顺序,就可以直接模拟了 记录历史最大值 用一个pair(a,b):之后加上a个,期间最大值为增加b个 合并? A1+A2= ...

  6. 2019.2.25 模拟赛T1【集训队作业2018】小Z的礼物

    T1: [集训队作业2018]小Z的礼物 我们发现我们要求的是覆盖所有集合里的元素的期望时间. 设\(t_{i,j}\)表示第一次覆盖第i行第j列的格子的时间,我们要求的是\(max\{ALL\}\) ...

  7. [集训队作业2018]蜀道难——TopTree+贪心+树链剖分+链分治+树形DP

    题目链接: [集训队作业2018]蜀道难 题目大意:给出一棵$n$个节点的树,要求给每个点赋一个$1\sim n$之内的权值使所有点的权值是$1\sim n$的一个排列,定义一条边的权值为两端点权值差 ...

  8. UOJ#422. 【集训队作业2018】小Z的礼物

    #422. [集训队作业2018]小Z的礼物 min-max容斥 转化为每个集合最早被染色的期望时间 如果有x个选择可以染色,那么期望时间就是((n-1)*m+(m-1)*n))/x 但是x会变,中途 ...

  9. UOJ#428. 【集训队作业2018】普通的计数题

    #428. [集训队作业2018]普通的计数题 模型转化好题 所以变成统计有标号合法的树的个数. 合法限制: 1.根标号比子树都大 2.如果儿子全是叶子,数量B中有 3.如果存在一个儿子不是叶子,数量 ...

随机推荐

  1. OpenCV 初体验

    个人博客原文链接 个人掘金链接 本文简单地介绍计算机图形处理的一些基本概念,以及一些有趣的例子和对应的Open CV的代码操作. 顺便说一句,恭喜IG夺冠! 一.图片存储原理 1.颜色空间RGB (1 ...

  2. 二进制数据的序列化反序列化和Json的序列化反序列化的重要区别

    前言:最近一个一个很奇怪的问题,很明白的说,就是没看懂,参照下面的代码: /// <summary> /// 反序列化对象 /// </summary> /// <typ ...

  3. 零基础学Python--------第9章 异常处理及程序调试

    第9章 异常处理及程序调试 9.1 异常概述 在程序运行过程中,经常会遇到各种各样的错误,这些错误统称为“异常”.这些异常有的是由于开发者将关键字敲错导致的,这类错误多数产生的是SyntaxError ...

  4. IntelliJ IDEA下的使用git

    1.git简介 git是目前流行的分布式版本管理系统.它拥有两套版本库,本地库和远程库,在不进行合并和删除之类的操作时这两套版本库互不影响.也因此其近乎所有的操作都是本地执行,所以在断网的情况下任然可 ...

  5. ASP.NET Core 部署IIS及 OFFSET 附近有语法错误解决

    今天自己开发了一个订机票的微信公众号,功能基本已经完成,然后想部署到服务器实际测试下.结果部署上去出现各种问题.先安装asp.net core模块,然后发现数据库并不像在开发时一样,执行ef的命令行语 ...

  6. arcgis api for js入门开发系列二十打印地图的那些事

    前面我写过关于利用arcgis api for js打印地图的,但是打印地图服务都是基于arcgis server发布的,arcgis api加载在线地图,比如天地图.百度地图.高德地图等,底图都是打 ...

  7. (办公)springmvc->controller的统一异常层,返回json

    controller里面写的代码,很多时候,没有写try{}catch(Exceiption ex){},结果就是系统出错,就算是接口,参数正确也会返回404,这个是不应该的. 下面是代码,以后参考 ...

  8. Redis 主从复制原理及雪崩 穿透问题

    定义: Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发工作由VMw ...

  9. Vue.js01:跑马灯效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. Exchange 2010邮件服务器的搭建和部署

    Exchange主要是针对内部网或者企业网用户进行搭建的邮件服务器软件,利用它能够很快地搭建安全性较高的内部网邮件系统. 本次搭建在个人环境中实践,纯属爱好折腾,分四步骤,1.搭建windows 20 ...