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 在数据加密和数据压缩中 ...
随机推荐
- java反射意义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- CSDN日报20170423 ——《私活,永远挽救不了自己屌丝的人生!》
[程序人生]私活,永远挽救不了自己屌丝的人生! 作者:北漂周 大多数接私活的人.是压根不知道下班后的时间他能够干什么!看剧?追星?逛街?当然,与这些对照,私活是一个更好的选择. 假设有这个时间,为什么 ...
- C++二叉树笔试题
#include <iostream> #include <stack> #include <queue> using namespace std; templat ...
- SpringMVC实战(三种映射处理器)
1.前言 上一篇博客,简单的介绍了一下SpringMVC的基础知识,这篇博客来说一下SpringMVC中的几种映射处理器机制. 2.三种映射处理器 2.1 BeanNameUrlHandlerMapp ...
- 5187: [Usaco2018 Jan]Sprinklers
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #inc ...
- [codeforces contest 1119 F] Niyaz and Small Degrees 解题报告 (树形DP+堆)
interlinkage: http://codeforces.com/contest/1119/problem/F description: 有一颗$n$个节点的树,每条边有一个边权 对于一个$x$ ...
- vcpkg错误分析方法
最近在使用vcpkg时,经常会碰到CMake错误. 有些以前能编译通过的包, 过一段时间又不能编译错误了. 错误提示一般是CMake错误, 弄得很郁闷. 我采用以下步骤解决了问题: 分析错误 查看错误 ...
- QlikSense移动端使用攻略
公司内部署QlikSense服务器,除了在电脑上用浏览器访问,也可以在移动端进行访问. 移动端访问在如下网址有英文详细介绍:https://community.qlik.com/docs/DOC-19 ...
- 初探.net framework 下的异步多线程
初探.net framework 下的异步多线程 目录 1.多线程的出现条件 2.Thread和ThreadPool的相关Api及用法 3.Task和Parallel的相关Api及用法 4.Async ...
- C-字符串和格式化输入\输出
1.字符串是一个或多个字符序列.字符串常量用双引号括起来“abc”,字符常量用单引号括起来‘’. 2.数组是同一类型的数据元素的有序序列.数据元素在内存中是连续存储的. C中没有为字符串定义专门的变量 ...