[UOJ86]mx的组合数——NTT+数位DP+原根与指标+卢卡斯定理
题目链接:
题目大意:给出四个数$p,n,l,r$,对于$\forall 0\le a\le p-1$,求$l\le x\le r,C_{x}^{n}\%p=a$的$x$的数量。$p<=3000$且保证$p$是质数,$n,l,r<=10^30$。
对于$10\%$的数据,可以直接杨辉三角推。
对于$20\%$的数据,因为$n$是确定的,可以递推出$C_{x+1}^{n}=C_{x}^{n}*\frac{x+1}{x+1-n}$。
对于另外$20\%$的数据,可以枚举$x$然后用$lucas$定理求。
对于另外$30\%$的数据,可以想到将问题转化成小于等于$r$的个数$-$小于等于$l-1$的个数。由$lucas$定理可知,$C_{x}^{n}\ mod\ p=\prod C_{b_{i}}^{a_{i}}\ mod\ p$,其中$a_{i},b_{i}$分别为$n,x$在$p$进制下的第$i$位。那么我们就可以用数位$DP$求,$f[i][j]$代表从最低为开始的前$i$位,每一位的值都不大于$b_{i}$且$\%p=j$的方案数;$g[i][j]$代表从最低为开始的前$i$位,每一位的值任意且$\%p=j$的方案数。设枚举第$i+1$位为$x$,$C_{x}^{a_{i+1}}=k$。那么可以得到$DP$转移方程$g[i+1][jk\ mod\ p]+=g[i][j]$,若$x<b_{i+1}$,则$f[i+1][jk\ mod\ p]+=g[i][j]$,若$x=b_{i+1}$,则$f[i+1][jk\ mod\ p]+=f[i][j]$。时间复杂度为$O(p^2log_{p})$。
对于$100\%$的数据,我们考虑优化上述$DP$,我们拿其中第一个转移方程来说(后两个同理),我们设$h[k]=\sum\limits_{x=0}^{p-1}[C_{x}^{a_{i+1}}==k]$。可以发现转移可以看成是$G[j*k\ mod\ p]=\sum\limits_{j=0}^{p-1}g[j]\sum\limits_{k=0}^{p-1}h[k]$,这和卷积式子很像,但他是乘法卷积,我们想办法将它变成加法卷积:因为$p$是质数,那么$p$一定有原根(设为$g$),也就是说对于任意$j$,其中$1\le j\le p-1$都有指标。我们设它的指标为$ind(j)$,那么$j*k\ mod\ p$就能转化为$g^{(ind(j)+ind(k))\ mod\ (p-1)}\ mod\ p$。这样我们就能用$FFT$或$NTT$来加速$DP$了,但注意到$0$没有指标,我们在转移时先忽略$0$,在最后输出答案时用总个数减掉其他答案就是$\%p=0$的个数了。注意原根从$1$开始枚举。至于$10^{30}$可以用$\_\_int128$存。时间复杂度为$O(plog_{p}^2)$。
两种写法,读者自选。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
typedef __int128 int128;
#define MOD 998244353
using namespace std;
int p;
int128 l,r,n;
int pr[10];
int cnt;
int G;
int mx;
ll sum;
int ind[30010];
ll f[100000];
ll g[100000];
ll h[100000];
int a[200];
int b[200];
ll ans[30010];
int c[200][30010];
int mask=1;
ll s[100000];
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read_()
{
int x=0;
char c=nc();
while(c<48)
{
c=nc();
}
while(c>47)
{
x=(((x<<2)+x)<<1)+(c^48),c=nc();
}
return x;
}
int128 read()
{
int128 x=0;
char c=nc();
while(c<48)
{
c=nc();
}
while(c>47)
{
x=(((x<<2)+x)<<1)+(c^48),c=nc();
}
return x;
}
ll quick(int x,int y,int mod)
{
ll res=1ll;
while(y)
{
if(y&1)
{
res=res*x%mod;
}
y>>=1;
x=1ll*x*x%mod;
}
return res;
}
void NTT(ll *a,int len,int miku)
{
for(int k=0,i=0;i<len;i++)
{
if(i>k)
{
swap(a[i],a[k]);
}
for(int j=len>>1;(k^=j)<j;j>>=1);
}
for(int k=2;k<=len;k<<=1)
{
int t=k>>1;
int x=quick(3,(MOD-1)/k,MOD);
if(miku==-1)
{
x=quick(x,MOD-2,MOD);
}
for(int i=0;i<len;i+=k)
{
ll w=1;
for(int j=i;j<i+t;j++)
{
ll tmp=a[j+t]*w%MOD;
a[j+t]=(a[j]-tmp+MOD)%MOD;
a[j]=(a[j]+tmp)%MOD;
w=w*x%MOD;
}
}
}
if(miku==-1)
{
for(int i=0,t=quick(len,MOD-2,MOD);i<len;i++)
{
a[i]=a[i]*t%MOD;
}
}
}
void solve(int128 num)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(h,0,sizeof(h));
memset(a,0,sizeof(a));
int res=0;
for(int i=1;num;i++)
{
a[i]=num%p;
num/=p;
res=max(res,i);
}
mx=max(res,mx);
g[0]=f[0]=1ll;
for(int k=1;k<=mx;k++)
{
memset(h,0,sizeof(h));
memset(s,0,sizeof(s));
NTT(g,mask,1);
NTT(f,mask,1);
if(a[k]>=b[k])
{
h[ind[c[k][a[k]]]]++;
NTT(h,mask,1);
for(int i=0;i<mask;i++)
{
s[i]+=1ll*h[i]*f[i]%MOD;
s[i]%=MOD;
}
NTT(h,mask,-1);
h[ind[c[k][a[k]]]]--;
}
for(int i=b[k];i<a[k];i++)
{
h[ind[c[k][i]]]++;
}
NTT(h,mask,1);
for(int i=0;i<mask;i++)
{
s[i]+=1ll*h[i]*g[i]%MOD;
s[i]%=MOD;
}
NTT(h,mask,-1);
NTT(s,mask,-1);
memset(f,0,sizeof(f));
for(int i=0;i<mask;i++)
{
f[i%(p-1)]+=s[i];
f[i%(p-1)]%=MOD;
}
for(int i=max(b[k],a[k]);i<p;i++)
{
h[ind[c[k][i]]]++;
}
NTT(h,mask,1);
for(int i=0;i<mask;i++)
{
s[i]=1ll*h[i]*g[i]%MOD;
}
NTT(s,mask,-1);
memset(g,0,sizeof(g));
for(int i=0;i<mask;i++)
{
g[i%(p-1)]+=s[i];
g[i%(p-1)]%=MOD;
}
}
}
int main()
{
p=read_(),n=read(),l=read(),r=read();
l--;
int s=p-1;
while(mask<(p<<1))
{
mask<<=1;
}
for(int i=2;i*i<=s;i++)
{
if(s%i==0)
{
pr[++cnt]=i;
while(s%i==0)
{
s/=i;
}
}
}
if(s!=1)
{
pr[++cnt]=s;
}
for(int i=1;i<p;i++)
{
bool flag=true;
for(int j=1;j<=cnt;j++)
{
if(quick(i,(p-1)/pr[j],p)==1)
{
flag=false;
break;
}
}
if(flag)
{
G=i;
break;
}
}
sum=1ll;
for(int i=0;i<p-1;i++)
{
ind[sum]=i;
sum*=G,sum%=p;
}
int128 N=n;
for(int i=1;N;i++)
{
b[i]=N%p;
N/=p;
mx=max(mx,i);
}
for(int i=1;i<=mx;i++)
{
for(int j=0;j<b[i];j++)
{
c[i][j]=0;
}
sum=1ll;
for(int j=b[i];j<p;j++)
{
c[i][j]=sum;
sum*=(j+1),sum%=p;
sum*=quick(j+1-b[i],p-2,p),sum%=p;
}
}
solve(l);
for(int i=0;i<p-1;i++)
{
ans[quick(G,i,p)]-=f[i];
}
for(int i=1;i<=p-1;i++)
{
ans[i]=(ans[i]%MOD+MOD)%MOD;
}
solve(r);
for(int i=0;i<p-1;i++)
{
ans[quick(G,i,p)]+=f[i];
}
for(int i=1;i<=p-1;i++)
{
ans[i]%=MOD;
}
ans[0]=(r-l)%MOD;
for(int i=1;i<p;i++)
{
ans[0]-=ans[i];
ans[0]=(ans[0]%MOD+MOD)%MOD;
}
for(int i=0;i<p;i++)
{
printf("%lld\n",ans[i]);
}
}
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
typedef __int128 int128;
#define MOD 998244353
using namespace std;
int p;
int128 l,r,n;
int pr[10];
int cnt;
int G;
int mx;
ll sum;
int ind[30010];
ll f[100000];
ll g[100000];
ll A[100000];
ll B[100000];
ll C[100000];
int a[200];
int b[200];
ll ans[30010];
int c[200][30010];
int mask=1;
int s[100000];
int pw[300010];
int fac[300010];
int inv[300010];
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read_()
{
int x=0;
char c=nc();
while(c<48)
{
c=nc();
}
while(c>47)
{
x=(((x<<2)+x)<<1)+(c^48),c=nc();
}
return x;
}
int128 read()
{
int128 x=0;
char c=nc();
while(c<48)
{
c=nc();
}
while(c>47)
{
x=(((x<<2)+x)<<1)+(c^48),c=nc();
}
return x;
}
ll quick(int x,int y,int mod)
{
ll res=1ll;
while(y)
{
if(y&1)
{
res=res*x%mod;
}
y>>=1;
x=1ll*x*x%mod;
}
return res;
}
void NTT(ll *a,int len,int miku)
{
for(int k=0,i=0;i<len;i++)
{
if(i>k)
{
swap(a[i],a[k]);
}
for(int j=len>>1;(k^=j)<j;j>>=1);
}
for(int k=2;k<=len;k<<=1)
{
int t=k>>1;
int x=quick(3,(MOD-1)/k,MOD);
if(miku==-1)
{
x=quick(x,MOD-2,MOD);
}
for(int i=0;i<len;i+=k)
{
ll w=1;
for(int j=i;j<i+t;j++)
{
ll tmp=a[j+t]*w%MOD;
a[j+t]=(a[j]-tmp+MOD)%MOD;
a[j]=(a[j]+tmp)%MOD;
w=w*x%MOD;
}
}
}
if(miku==-1)
{
for(int i=0,t=quick(len,MOD-2,MOD);i<len;i++)
{
a[i]=a[i]*t%MOD;
}
}
}
void solve(int128 num)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(a,0,sizeof(a));
int res=0;
for(int i=1;num;i++)
{
a[i]=num%p;
num/=p;
res=max(res,i);
}
mx=max(res,mx);
g[1]=f[1]=1ll;
for(int k=1;k<=mx;k++)
{
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
for(int i=b[k];i<p;i++)
{
if(c[k][i])
{
A[ind[c[k][i]]]++;
}
}
for(int i=1;i<p;i++)
{
B[ind[i]]+=g[i];
B[ind[i]]%=MOD;
}
NTT(A,mask,1);
NTT(B,mask,1);
for(int i=0;i<mask;i++)
{
C[i]=A[i]*B[i]%MOD;
}
NTT(C,mask,-1);
memset(g,0,sizeof(g));
for(int i=0;i<mask;i++)
{
(g[quick(G,i%(p-1),p)]+=C[i])%=MOD;
}
memset(A,0,sizeof(A));
for(int i=b[k];i<a[k];i++)
{
if(c[k][i])
{
A[ind[c[k][i]]]++;
}
}
NTT(A,mask,1);
for(int i=0;i<mask;i++)
{
C[i]=A[i]*B[i]%MOD;
}
NTT(C,mask,-1);
memset(s,0,sizeof(s));
for(int i=0;i<mask;i++)
{
(s[quick(G,i%(p-1),p)]+=C[i])%=MOD;
}
if(c[k][a[k]])
{
for(int i=1;i<p;i++)
{
(s[c[k][a[k]]*i%p]+=f[i])%=MOD;;
}
}
for(int i=1;i<p;i++)
{
f[i]=s[i];
}
}
}
int get_ori(int p)
{
int s=p-1;
for(int i=2;i*i<=s;i++)
{
if(s%i==0)
{
pr[++cnt]=i;
while(s%i==0)
{
s/=i;
}
}
}
if(s!=1)
{
pr[++cnt]=s;
}
for(int i=1;i<p;i++)
{
bool flag=true;
for(int j=1;j<=cnt;j++)
{
if(quick(i,(p-1)/pr[j],p)==1)
{
flag=false;
break;
}
}
if(flag)
{
return i;
break;
}
}
}
int main()
{
p=read_(),n=read(),l=read(),r=read();
while(mask<(p<<1))
{
mask<<=1;
}
G=get_ori(p);
pw[0]=1ll;
for(int i=1;i<p;i++)
{
pw[i]=pw[i-1]*G%p;
}
sum=1ll;
for(int i=0;i<p-1;i++)
{
ind[sum]=i;
sum*=G,sum%=p;
}
int128 N=n;
for(int i=1;N;i++)
{
b[i]=N%p;
N/=p;
mx=max(mx,i);
}
fac[0]=inv[0]=1ll;
for(int i=1;i<p;i++)
{
fac[i]=fac[i-1]*i%p;
}
inv[p-1]=quick(fac[p-1],p-2,p);
for(int i=p-2;i>=1;i--)
{
inv[i]=inv[i+1]*(i+1)%p;
}
for(int i=1;i<=120;i++)
{
for(int j=b[i];j<p;j++)
{
c[i][j]=fac[j]*inv[j-b[i]]%p*inv[b[i]]%p;
}
}
solve(r);
for(int i=1;i<p;i++)
{
ans[i]=f[i];
}
solve(l-1);
for(int i=1;i<p;i++)
{
ans[i]=((ans[i]-f[i])%MOD+MOD)%MOD;
}
ans[0]=(r-l+1)%MOD;
for(int i=1;i<p;i++)
{
ans[0]=((ans[0]-ans[i])%MOD+MOD)%MOD;
}
for(int i=0;i<p;i++)
{
printf("%lld\n",ans[i]);
}
}
[UOJ86]mx的组合数——NTT+数位DP+原根与指标+卢卡斯定理的更多相关文章
- uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT)
uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT) uoj 题目描述自己看去吧( 题解时间 首先看到 $ p $ 这么小还是质数,第一时间想到 $ lucas $ 定理. 注意 ...
- [Swust OJ 715]--字典序问题(组合数预处理/数位dp)
题目链接:http://acm.swust.edu.cn/problem/715/ Time limit(ms): 1000 Memory limit(kb): 65535 在数据加密和数据压缩中 ...
- UOJ#275. 【清华集训2016】组合数问题 数位dp
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ275.html 题解 用卢卡斯定理转化成一个 k 进制意义下的数位 dp 即可. 算答案的时候补集转化一下 ...
- BZOJ 3209 花神的数论题 数位DP+数论
题目大意:令Sum(i)为i在二进制下1的个数 求∏(1<=i<=n)Sum(i) 一道非常easy的数位DP 首先我们打表打出组合数 然后利用数位DP统计出二进制下1的个数为x的数的数量 ...
- 数位dp/记忆化搜索
一.引例 #1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an ...
- UOJ #86 mx的组合数 (数位DP+NTT+原根优化)
题目传送门 matthew99神犇的题解讲得非常清楚明白,跪烂Orzzzzzzzzzzzzz 总结一下,本题有很多重要的突破口 1.Lucas定理 看到n,m特别大但模数特别小时,容易想到$lucas ...
- 【20181031T2】几串字符【数位DP思想+组合数】
题面 [错解] 一眼数位DP 设\(f(i,c00,c01,c10,c11)\)-- 神tm DP 哎好像每两位就一定对应c中的一个,那不用记完 所以可以设\(f(i,c00,c01,c10)\)-- ...
- BZOJ_3209_花神的数论题_组合数+数位DP
BZOJ_3209_花神的数论题_组合数+数位DP Description 背景 众所周知,花神多年来凭借无边的神力狂虐各大 OJ.OI.CF.TC …… 当然也包括 CH 啦. 描述 话说花神这天又 ...
- [BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT)
[BZOJ 3992] [SDOI 2015] 序列统计(DP+原根+NTT) 题面 小C有一个集合S,里面的元素都是小于质数M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数 ...
随机推荐
- JenKins自动化构建部署流程参考
Jenkins工作流程 程序员提交代码到Git/SVN仓库,触发钩子程序向 JenKins 进行通知,Jenkins 调用Git/SVN插件获取源码,调用Maven打包为war包,调用Deploy t ...
- 如何向微软 Docs 和本地化社区提交翻译贡献
Docs (docs.microsoft.com)是微软新版的文档网站,重新规划了各项技术栈的文档结构,看起来比 MSDN 可读性更好.虽然 Docs 提供了各种语言的版本,但大多是机器翻译,某些中文 ...
- Java 控制语句:循环、条件判断
基础很重要,基础很重要,基础很重要.重要的事情说三遍,. 程序设计中的控制语句主要有三种:顺序.分支和循环.我们每天写的代码,除了业务相关,里面会包含大量的控制语句.但是控制语句的基本使用,是否有些坑 ...
- 领域驱动设计(DDD:Domain-Driven Design) 介绍
Eric Evans的“Domain-Driven Design领域驱动设计”简称DDD,Evans DDD是一套综合软件系统分析和设计的面向对象建模方法,本站Jdon.com是国内公开最早讨论DDD ...
- python-PyQuery详解
PyQuery库也是一个非常强大又灵活的网页解析库,如果你有前端开发经验的,都应该接触过jQuery,那么PyQuery就是你非常绝佳的选择,PyQuery 是 Python 仿照 jQuery 的严 ...
- group by用法
select * from Table group by id,一定不能是*,而是某一个列或者某个列的聚合函数. 参考:http://www.cnblogs.com/jingfengling/p/59 ...
- git在vs2017中的使用
对于习惯了右键提交源代码的道友来说,敲命令行真的蓝瘦香菇.所幸17里集成了Git插件,用起来还是挺方便的. 1.本地安装git,工具还是要有的,主要用于配置环境,ssh配置一下.就不用每次都去连接了. ...
- JS XMLHttpRequesst对象 http post的五种请求状态
记录一下js中对http请求的几种状态,下附代码 readyState 存有 XMLHttpRequest 的状态.从 0 到 4 发生变化. 0: 请求未初始化 1: 服务器连接已建立 2: 请求已 ...
- 【Python3练习题 013】 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字
a=input('输入数字>>>') count=int(input('几个数字相加>>>')) ret=[] for i in range(1,count+1): ...
- oracle查询不走索引的一些情况(索引失效)
Oracle建立索引的目的是为了避免全表扫描,提高查询的效率. 但是有些情况下发现即使建立了索引,但是写出来的查询还是很慢,然后会发现是索引失效导致的,所以需要了解一下那些情况会导致索引失效,即查询不 ...