UOJ #86 mx的组合数 (数位DP+NTT+原根优化)
matthew99神犇的题解讲得非常清楚明白,跪烂Orzzzzzzzzzzzzz
总结一下,本题有很多重要的突破口
1.Lucas定理
看到n,m特别大但模数特别小时,容易想到$lucas$定理
$C_{n}^{m}=C_{n/p}^{m/p}\cdot C_{n\;mod\;p}^{m\;mod\;p}\;(mod\;p)$
但普通的$lucas$显然不适用于多次计算,我们可以把$lucas$定理展开
我们把$n$和$m$都看成两个$p$进制数$a$和$b$
$C_{n}^{m}=\pi C_{a_{i}}^{b_{i}}\;(mod\;p)$
这个展开显然成立
2.数位$DP$
想到了上一条进制转化,很容易就联想到数位$DP$
定义$f[i][j]$表示枚举到第$i$位,余数是$j$的方案数
转移十分经典,设现在要填上的数是$x$,$0$表示没达到上界,$1$达到上界,设$z=C_{x}^{b_{i+1}}$
$f0[i][j\cdot z\;mod\;p]+=f0[i][j]$
$x<a_{i+1}$时,$f0[i][j\cdot z\;mod\;p]+=f1[i][j]$
$x=a_{i+1}$时,$f1[i][j\cdot z\;mod\;p]+=f1[i][j]$
总复杂度$O(p^2log_{p}n)$
3.原根优化与多项式乘法
上面的$p^{2}$转移咋这么无脑呢,是不是有啥优化啊?是的
由于$p$是一个质数,它一定存在一个原根$g$
这就要涉及到离散对数了。其实这是一个映射
$g^{ind(x)}\equiv x\;(mod\;p)$
$ind(x)\;(ind(x)\in[0,p-1))$与$x\;(x\in [1,p))$是一组一一映射
那么$j\cdot z\;mod\;p$
$=g^{ind(j)+ind(z)\;(mod\;p-1)}\;mod\;p$
我们利用映射关系,把底数化成指数
这样转移变成了卷积的形式,用多项式乘法每次$O(plogp)$计算
计算出结果后,再逆映射回来得到实际的答案
而离散对数的映射并不能处理余数等于$0$的情况,我们每次$O(p)$单独讨论即可
总时间$O(plogplog_{p}n)$
#include <bits/stdc++.h>
#define N1 (1<<16)+10
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define i128 __int128
using namespace std; const int inf=0x3f3f3f3f;
i128 gi128()
{
i128 ret=;int fh=;char c=getchar();
while(c<''||c>''){if(c=='-')fh=-;c=getchar();}
while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
return ret*fh;
}
ll qpow(ll x,ll y,const int &p)
{
ll ans=;
for(;y;x=x*x%p,y>>=) if(y&) ans=ans*x%p;
return ans;
}
const ll p=;
namespace NTT{
ll a[N1],b[N1],c[N1],Wn[N1],_Wn[N1];
int r[N1];
ll qpow(ll x,ll y)
{
ll ans=;
for(;y;x=x*x%p,y>>=) if(y&) ans=ans*x%p;
return ans;
}
void NTT(ll *s,int len,int type)
{
int i,j,k; ll wn,w,t;
for(i=;i<len;i++) if(i<r[i]) swap(s[i],s[r[i]]);
for(k=;k<=len;k<<=)
{
wn=(type>)?Wn[k]:_Wn[k];
for(i=;i<len;i+=k)
{
for(j=,w=;j<(k>>);j++,w=w*wn%p)
{
t=w*s[i+j+(k>>)]%p;
s[i+j+(k>>)]=(s[i+j]+p-t)%p;
s[i+j]=(s[i+j]+t)%p;
}
}
}
}
void Pre(int len,int L)
{
int i;
for(i=;i<len;i++) r[i]=(r[i>>]>>)|((i&)<<(L-));
for(i=;i<=len;i<<=) Wn[i]=qpow(,(p-)/i), _Wn[i]=qpow(Wn[i],p-);
}
void Main(int len)
{
int i,invl=qpow(len,p-);
NTT(a,len,); NTT(b,len,);
for(i=;i<len;i++) c[i]=a[i]*b[i]%p;
NTT(c,len,-);
for(i=;i<len;i++) c[i]=c[i]*invl%p;
memset(a,,sizeof(a)); memset(b,,sizeof(b));
}
}; int T,m,G;
i128 n,l,r;
int a[N1],b[N1],tmp[N1];
ll f0[][N1],f1[][N1],mul[N1],_mul[N1],ans[N1]; inline ll C(int N,int M)
{
if(M>N) return ;
return mul[N]*_mul[M]%m*_mul[N-M]%m;
} int pr[N1],use[N1],ind[N1],_ind[N1],son[N1];
void get_ind()
{
int i,j,ns=,flag,x,cnt=,sz=;
for(i=;i<=m-;i++)
{
if(!use[i]) pr[++cnt]=i;
for(j=;j<=cnt&&i*pr[j]<=m-;j++)
{
use[i*pr[j]]=;
if(i%pr[j]==) break;
}
}
for(j=;j<=cnt;j++) if((m-)%pr[j]==) son[++sz]=(m-)/pr[j];
for(i=;i<=m-;i++)
{
flag=;
for(j=;j<=sz;j++) if(qpow(i,son[j],m)==){ flag=; break;}
if(flag){ G=i; break; }
}
ind[]=; _ind[]=; ind[]=m-;
for(i=,x=;i<=m-;i++) x=x*G%m, ind[x]=i, _ind[i]=x;
}
//using NTT::a; using NTT::b; using NTT::c;
void solve(i128 w,int type)
{
int i,j,k,nw=,nn=,len,L,now=,pst=;
if(w<n){ ans[]=((w+)*type%p+ans[]+p)%p; return; }
while(w>) nw++,tmp[nw]=w%m,w/=m;
for(i=;i<=nw;i++) a[nw-i+]=tmp[i];
i128 N=n;
while(N>) nn++,tmp[nn]=N%m,N/=m;
for(i=;i<=nn;i++) b[nw-i+]=tmp[i]; for(len=,L=;len<m+m-;len<<=,L++);
NTT::Pre(len,L); memset(f0,,sizeof(f0)); memset(f1,,sizeof(f1));
for(j=;j<a[];j++) f0[][C(j,b[])]++;
f1[][C(a[],b[])]++;
for(i=;i<nw;i++)
{
memset(f0[now],,sizeof(f0[now])); memset(f1[now],,sizeof(f1[now])); for(j=;j<m;j++) NTT::a[ind[j]]=f0[pst][j];
for(j=;j<m;j++) NTT::b[ind[C(j,b[i+])]]++;
for(j=;j<m;j++) (f0[now][]+=f0[pst][j]*NTT::b[m-])%=p;
(f0[now][]+=f0[pst][]*m)%=p; NTT::b[m-]=;
NTT::Main(len);
for(j=;j<len;j++) (f0[now][_ind[j%(m-)]]+=NTT::c[j])%=p; for(j=;j<m;j++) NTT::a[ind[j]]=f1[pst][j];
for(j=;j<a[i+];j++) NTT::b[ind[C(j,b[i+])]]++;
for(j=;j<m;j++) (f0[now][]+=f1[pst][j]*NTT::b[m-])%=p;
(f0[now][]+=f1[pst][]*a[i+])%=p; NTT::b[m-]=;
NTT::Main(len);
for(j=;j<len;j++) (f0[now][_ind[j%(m-)]]+=NTT::c[j])%=p; for(k=;k<m;k++)
(f1[now][k*C(a[i+],b[i+])%m]+=f1[pst][k])%=p; swap(now,pst);
}
for(i=;i<m;i++) (ans[i]+=(f0[pst][i]+f1[pst][i])*type%p+p)%=p;
} int main()
{
int i,j,x,cnt=;
scanf("%d",&m); n=gi128(); l=gi128(); r=gi128(); l--;
mul[]=mul[]=_mul[]=_mul[]=;
for(i=;i<m;i++) mul[i]=mul[i-]*i%m, _mul[i]=qpow(mul[i],m-,m);
get_ind();
solve(r,);
solve(l,-);
for(i=;i<m;i++) printf("%lld\n",ans[i]);
return ;
}
UOJ #86 mx的组合数 (数位DP+NTT+原根优化)的更多相关文章
- BZOJ_3209_花神的数论题_组合数+数位DP
BZOJ_3209_花神的数论题_组合数+数位DP Description 背景 众所周知,花神多年来凭借无边的神力狂虐各大 OJ.OI.CF.TC …… 当然也包括 CH 啦. 描述 话说花神这天又 ...
- 【BZOJ 3326】[Scoi2013]数数 数位dp+矩阵乘法优化
挺好的数位dp……先说一下我个人的做法:经过观察,发现这题按照以往的思路从后往前递增,不怎么好推,然后我就大胆猜想,从前往后推,发现很好推啊,维护四个变量,从开始位置到现在有了i个数 f[i]:所有数 ...
- BZOJ.3992.[SDOI2015]序列统计(DP NTT 原根)
题目链接 \(Description\) 给定\(n,m,x\)和集合\(S\).求\(\prod_{i=1}^na_i\equiv x\ (mod\ m)\)的方案数.其中\(a_i\in S\). ...
- [UOJ 275/BZOJ4737] 【清华集训2016】组合数问题 (LUCAS定理的运用+数位DP)
题面 传送门:UOJ Solution 这题的数位DP好蛋疼啊qwq 好吧,我们说回正题. 首先,我们先回忆一下LUCAS定理: \(C_n^m \equiv C_{n/p}^{m/p} \times ...
- uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT)
uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT) uoj 题目描述自己看去吧( 题解时间 首先看到 $ p $ 这么小还是质数,第一时间想到 $ lucas $ 定理. 注意 ...
- [UOJ86]mx的组合数——NTT+数位DP+原根与指标+卢卡斯定理
题目链接: [UOJ86]mx的组合数 题目大意:给出四个数$p,n,l,r$,对于$\forall 0\le a\le p-1$,求$l\le x\le r,C_{x}^{n}\%p=a$的$x$的 ...
- UOJ#275. 【清华集训2016】组合数问题 数位dp
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ275.html 题解 用卢卡斯定理转化成一个 k 进制意义下的数位 dp 即可. 算答案的时候补集转化一下 ...
- uoj#275. 【清华集训2016】组合数问题(数位dp)
传送门 假设有\(k|{n\choose m}\),因为\(n!\)中质因子\(k\)的次数为\(S(n)=\left\lfloor\frac{n}{k}\right\rfloor+\left\lfl ...
- [Swust OJ 715]--字典序问题(组合数预处理/数位dp)
题目链接:http://acm.swust.edu.cn/problem/715/ Time limit(ms): 1000 Memory limit(kb): 65535 在数据加密和数据压缩中 ...
随机推荐
- [bzoj1195][HNOI2006]最短母串_动态规划_状压dp
最短母串 bzoj-1195 HNOI-2006 题目大意:给一个包含n个字符串的字符集,求一个字典序最小的字符串使得字符集中所有的串都是该串的子串. 注释:$1\le n\le 12$,$1\le ...
- 部署WAR包实时查看Tomcat的状态和日志
在不重启Tomcat的情况下部署WAR包实时输出日志的方法: 注意:以下方式只适合Linux. 一.定位错误 查看Tomcat日志的尾部 tail -n 50 /opt/tomcat8/logs/ca ...
- JAVAEE之-----MySQL分页技术(带搜索)
需求: 为什么须要採用分页技术呢?在数据库中我们查询数据的时候,须要将数据返回到显示页面.数据库中含有大量数据,所有显示在一个页面过于太多,所以我们须要採用分页技术.每一页显示不同数据. 主要解决这个 ...
- [HDU 1421]搬寝室(富有新意的DP)
题目地址:pid=1421" target="_blank">http://acm.hdu.edu.cn/showproblem.php? pid=1421 题目大 ...
- Xamarin Mono For Android、Monotouch 安装
一.Windows下面的安装 1. 安装环境介绍: Win8.1 企业版64位或Win7 64.VS2013 update4 2. 安装jdk 到oracle官方下载jdk-8u45-wi ...
- luogu3376 【模板】 网络最大流
题目大意 给出一个网络图,以及其源点和汇点,求出其网络最大流. 概念 可以把网络图看作管道,节点看作管道的交界处.流就像是管道里的流水.管道有个容量(相当于横截面积),还会有个流量(相当于水流占了管道 ...
- Android平台Overlay机制【转】
本文转载自:http://blog.csdn.net/wh_19910525/article/details/39254815 Android overlay 机制允许在不修改packages中apk ...
- C语言 - .c和.h文件的困惑
本质上没有任何区别. 只不过一般:.h文件是头文件,内含函数声明.宏定义.结构体定义等内容. .c文件是程序文件,内含函数实现,变量定义等内容.而且是什么后缀也没有关系,只不过编译器会默认对某些后缀的 ...
- HUST 1585 排队
2019-05-21 10:15:00 加油,加油 !!! #include <bits/stdc++.h> using namespace std; int main() { int n ...
- Redis(一)、Redis五种数据结构
Redis五种数据结构如下: 对redis来说,所有的key(键)都是字符串. 1.String 字符串类型 是redis中最基本的数据类型,一个key对应一个value. String类型是二进制安 ...