[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的数列,数 ...
随机推荐
- 多模块后带来的问题解决方法 - OSGI原形(.NET)
目前只做了基础的功能,比如: 各个模块单独的AppDomain容器 Activator激活 导出的服务检查 不过,虽说这样,但目前的这个版本已经能实现模块分离.互相依赖调用等功能了,对模块划分已经有很 ...
- Item 22: 当使用Pimpl机制时,在实现文件中给出特殊成员函数的实现
本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你曾经同过久的编译时间斗争过,那么你肯定对Pi ...
- flink1.7自定义source实现
flink读取source data 数据的来源是flink程序从中读取输入的地方.我们可以使用StreamExecutionEnvironment.addSource(sourceFunction) ...
- Java基础之数据比较Integer、Short、int、short
基础很重要,基础很重要,基础很重要.重要的事情说三遍,. 今天聊一聊Java的数据比较,这个范围比较大,基础类型的比较.引用类型的比较. 前提: 1.Java和c#都提供自动装箱和自动拆箱操作,何为自 ...
- (第十三周)Final Review会议
项目名:食物链教学工具 组名:奋斗吧兄弟 组长:黄兴 组员:李俞寰.杜桥.栾骄阳.王东涵 Final Review会议 时间:2016.12.2 13:00——15:00 地点:冬华楼一楼大厅 会 ...
- Vicious Keyboard CodeForces - 801A (暴力+模拟)
题目链接 题意: 给定一个字符串,最多更改一个字符,问最多可以有多少个“VK”子串? 思路: 由于数据量很小,不妨尝试暴力写.首先算出不更改任何字符的情况下有多个VK字串,然后尝试每一次更改一个位置的 ...
- 亲测可以永久破解2018版本的pycharm
pycharm是很强大的开发工具,但是每次注册着实让人头疼.网络上很多注册码.注册服务器等等.但都只是一年或者不能用:为次有如下解决方案.亲测有效!!! 如果想让pycharm永久被激活,比如截止日到 ...
- 【kindle笔记】之 《鬼吹灯》-9-20
[kindle笔记]读书记录-总 9-20 日常吐槽 连着几天,基本是一口气读完了鬼吹灯. 想来,也算是阴差阳错了.本来是想看盗墓的,读了几页开头,心想坏了,拷贝错了,这是鬼吹灯-- 讲真的,每每读小 ...
- Mysql drop function xxxx ERROR 1305 (42000): FUNCTION (UDF) xxxx does not exist
mysql> drop function GetEmployeeInformationByID;ERROR 1305 (42000): FUNCTION (UDF) GetEmployeeInf ...
- python生成个性二维码学习笔记
在linux环境下进行编码 1.先进家目录,自行创建Code文件夹 cd Code 2.下载MyQR库 sudo pip3 install MyQR 3.下载所需资源文件并解压 Code/ $ wge ...