uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT)
uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT)
题目描述自己看去吧(
题解时间
首先看到 $ p $ 这么小还是质数,第一时间想到 $ lucas $ 定理。
注意 $ lucas $ 定理的另外一种写法是将数转换为 $ p $ 进制后计算$ C_{n}^{m} = \Pi C_{a_i}^{b_i} $
所以考虑对于 $ l-1 $ 和 $ r $ 各进行一次数位 $ dp $ 。
$ dp[i][j] $表示从低位起算到 $ i $ 位计算结果取模后为 $ j $ 且保证是在合法范围以内的方案数
$ dg[i][j] $ 表示从低位起算到 $ i $ 位计算结果取模后为 $ j $ 且不保证是在合法范围以内的方案数
转移方法:
对于计算到某一位 $ i $
$ n $ 已经给定,也就是说 $ b_i $ 已经确定
所以枚举 $ x $ 值在这一位对应的 $ a_i $ 设为 $ k $ ,设 $ C_{k}^{b_i}=g $
转移:
$ dg[i][j \cdot g \ mod \ p]+=dg[i-1][j] $
$ dp[i][j \cdot g \ mod \ p]+=dg[i-1][j] ( k < a_{i_{max}} ) $
$ dp[i][j \cdot g \ mod \ p]+=dp[i-1][j] ( k = a_{i_{max}} ) $
时间复杂度$ p^{2}logn $。
这个暴力好像是有50分。
然后考虑优化。
(这么毒瘤咋考虑出来的啊)
上式中的 $ j \cdot g $ 可以考虑优化掉。
这时就如毒瘤的数学题一样,我们看到p是质数,考虑直接用指标把它降维就好了。。。(啥?)
还是考虑上面的dp方程。
我们现在枚举到i位,用上面第一个转移式为例。
设 $ f[x] = \sum \limits_{ k = 0 }^{ p - 1 } [ C_{k}^{b_i} == x] $
那么转移式变成一个乘法卷积 $ dg^{'} [i] = \sum \limits_{j=0}^{p-1} \sum \limits_{g=0}^{p-1} dg[j] * f[g] * [j \cdot g \ mod \ p == i] $
上指标之后$ dg^{'} [i] = \sum \limits_{j=0}^{p-1} \sum \limits_{g=0}^{p-1} dg[j] * f[g] * [ (ln[j]+ln[g]) \ mod \ \phi(p) == i] $
然后上NTT。
注意0没有指标,求出其他答案之后用总数减一下就能求出0的答案了。
#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
typedef __int128 llint;
template<typename TP>inline void read(TP &tar)
{
TP ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){ret=(TP)ret*10+ch-'0';ch=getchar();}
tar=ret*f;
}
namespace LarjaIX
{
const int N=70011,maxn=65536,P=30011,B=150;
const int mo=998244353,G=3;
lint fpow(lint a1,lint p1,lint m1);
void ntt(lint *f1,int tp);
int p,phi,len=1,g;llint n,l,r;
int rev[N];
int bitn[B],bitm[B],maxbit;
int fac[P],inv[P],facinv[P];
int ln[P];
int c[B][P];
lint ans[P];
lint f1[N],f2[N],dp[N],dg[N],dt[N];
lint wg[N],iwg[N];
void work(llint lim)
{
memset(dp,0,sizeof(dp));
memset(dg,0,sizeof(dg));
memset(bitn,0,sizeof(bitn));
for(int i=1;lim;i++) bitn[i]=lim%p,lim/=p,maxbit=max(maxbit,i);
dp[1]=dg[1]=1;
for(int b=1;b<=maxbit;b++)
{
memset(f1,0,sizeof(f1)),memset(f2,0,sizeof(f2));
for(int i=1;i<p;i++) f1[ln[i]]=dg[i];
for(int i=bitm[b];i<p;i++)if(c[b][i]) f2[ln[c[b][i]]]++;
ntt(f1,1),ntt(f2,1);
for(int i=0;i<len;i++) f2[i]=f1[i]*f2[i]%mo;
ntt(f2,-1);
memset(dg,0,sizeof(dg));
for(int i=0;i<len;i++) (dg[fpow(g,i%phi,p)]+=f2[i])%=mo;
memset(f2,0,sizeof(f2));
for(int i=bitm[b];i<bitn[b];i++)if(c[b][i]) f2[ln[c[b][i]]]++;
ntt(f2,1);
for(int i=0;i<len;i++) f2[i]=f1[i]*f2[i]%mo;
ntt(f2,-1);
memset(dt,0,sizeof(dt));
for(int i=0;i<len;i++) (dt[fpow(g,i%phi,p)]+=f2[i])%=mo;
if(c[b][bitn[b]])for(int i=1;i<p;i++) (dt[c[b][bitn[b]]*i%p]+=dp[i])%=mo;
memcpy(dp,dt,sizeof(dp));
}
}
int pri[P];
bool gck(int i){for(int j=1;j<=pri[0];j++) if(fpow(i,phi/pri[j],p)==1) return 0;return 1;}
int maid()
{
// freopen("sample.in","r",stdin);
// freopen("u.out","w",stdout);
read(p),read(n),read(l),read(r),phi=p-1;
//-----------------------------------------------------------------------------------------------------
fac[1]=fac[0]=inv[1]=facinv[1]=facinv[0]=1;
for(int i=2;i<p;i++) fac[i]=fac[i-1]*i%p,inv[i]=inv[p%i]*(p-p/i)%p,facinv[i]=facinv[i-1]*inv[i]%p;
//-----------------------------------------------------------------------------------------------------
while(len<=p*2) len<<=1;
for(int i=1;i<=len;i<<=1) wg[i]=fpow(G,(mo-1)/(i<<1),mo),iwg[i]=fpow(wg[i],mo-2,mo);
for(int i=1;i<len;i++) rev[i]=(rev[i>>1]>>1)|((len>>1)*(i&1));
//-----------------------------------------------------------------------------------------------------
//just get the g and ln of p
{
int tmp=phi;
for(int i=2;i*i<=tmp;i++)if(tmp%i==0)
{
pri[++pri[0]]=i;
while(tmp%i==0) tmp/=i;
}
if(tmp!=1) pri[++pri[0]]=tmp;
for(int i=1;i<p;i++) if(gck(i)){g=i;break;}
tmp=1;
for(int i=0;i<phi;i++) ln[tmp]=i,(tmp*=g)%=p;
}
//-----------------------------------------------------------------------------------------------------
{
llint tmp=n;
for(int bi=1;tmp;bi++) bitm[bi]=tmp%p,tmp/=p,maxbit=bi;//every bit of n
}
//-----------------------------------------------------------------------------------------------------
for(int i=1;i<128;i++)for(int j=bitm[i];j<p;j++) c[i][j]=fac[j]*facinv[j-bitm[i]]%p*facinv[bitm[i]]%p;
//-----------------------------------------------------------------------------------------------------
work(r);
for(int i=1;i<p;i++) ans[i]=dp[i];
work(l-1);
for(int i=1;i<p;i++) (ans[i]+=mo-dp[i])%=mo;
ans[0]=(r-l+1)%mo;
for(int i=1;i<p;i++) (ans[0]+=mo-ans[i])%=mo;
for(int i=0;i<p;i++) printf("%lld\n",ans[i]);
return 0;
}
lint fpow(lint a1,lint p1,lint m1)
{
lint ret=1;
while(p1)
{
if(p1&1ll) (ret*=a1)%=m1;
(a1*=a1)%=m1,p1>>=1;
}
return ret;
}
void ntt(lint *f1,int tp)
{
for(int i=0;i<len;i++) if(i<rev[i]) swap(f1[i],f1[rev[i]]);
lint ilen=fpow(len,mo-2,mo);
for(int i=1;i<len;i<<=1)
{
lint w0=~tp?wg[i]:iwg[i];
for(int j=0;j<len;j+=(i<<1))
{
lint w=1;
for(int k=0;k<i;k++,(w*=w0)%=mo)
{
lint w1=f1[j+k],w2=w*f1[j+k+i]%mo;
f1[j+k]=(w1+w2)%mo,f1[j+k+i]=(w1-w2+mo)%mo;
}
}
}
if(tp==-1) for(int i=0;i<len;i++) (f1[i]*=ilen)%=mo;
}
}
int main(){return LarjaIX::maid();}
uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT)的更多相关文章
- [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$的 ...
- BZOJ4737 组合数问题 【Lucas定理 + 数位dp】
题目 组合数C(n,m)表示的是从n个物品中选出m个物品的方案数.举个例子,从(1,2,3)三个物品中选择两个物品可以有( 1,2),(1,3),(2,3)这三种选择方法.根据组合数的定义,我们可以给 ...
- bzoj 1902: Zju2116 Christopher lucas定理 && 数位DP
1902: Zju2116 Christopher Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 172 Solved: 67[Submit][Stat ...
- [BZOJ4591][SHOI2015]超能粒子炮·改(Lucas定理+数位DP)
大组合数取模可以想到Lucas,考虑Lucas的意义,实际上是把数看成P进制计算. 于是问题变成求1~k的所有2333进制数上每一位数的组合数之积. 数位DP,f[i][0/1]表示从高到低第i位,这 ...
- BZOJ4737 组合数问题(卢卡斯定理+数位dp)
不妨不管j<=i的限制.由卢卡斯定理,C(i,j) mod k=0相当于k进制下存在某位上j大于i.容易想到数位dp,即设f[x][0/1][0/1][0/1]为到第x位时是否有某位上j> ...
- 【(好题)组合数+Lucas定理+公式递推(lowbit+滚动数组)+打表找规律】2017多校训练七 HDU 6129 Just do it
http://acm.hdu.edu.cn/showproblem.php?pid=6129 [题意] 对于一个长度为n的序列a,我们可以计算b[i]=a1^a2^......^ai,这样得到序列b ...
- [Swust OJ 247]--皇帝的新衣(组合数+Lucas定理)
题目链接:http://acm.swust.edu.cn/problem/0247/ Time limit(ms): 1000 Memory limit(kb): 65535 Descriptio ...
- BZOJ4591 SHOI2015超能粒子炮·改(卢卡斯定理+数位dp)
注意到模数很小,容易想到使用卢卡斯定理,即变成一个2333进制数各位组合数的乘积.对于k的限制容易想到数位dp.可以预处理一发2333以内的组合数及组合数前缀和,然后设f[i][0/1]为前i位是否卡 ...
- Codeforces 582D - Number of Binominal Coefficients(Kummer 定理+数位 dp)
Codeforces 题目传送门 & 洛谷题目传送门 一道数论与数位 dp 结合的神题 %%% 首先在做这道题之前你需要知道一个定理:对于质数 \(p\) 及 \(n,k\),最大的满足 \( ...
随机推荐
- java Excel 简单工具
我就简单的分享一下我常用的工具 这次由于个人问题工具注释全部乱码差点无法还原,也是为了防止数据丢失后期找不到再次保留方法把. 调用工具个别方法 <dependency> <group ...
- Xshell在Windows和Linux间文件的上传和下载
本文通过lrzsz来实现Windows和Linux间文件间的文件传输. lrzsz使用 XMODEM.YMODEM 和 ZMODEM 文件传输协议来实现文件的上传和下载.相比 FTP 或者 WinSC ...
- DNS中的FQDN
FQDN:(Fully Qualified Domain Name)全限定域名:同时带有主机名和域名的名称.(通过符号".") 例如:主机名是bigserver,域名是mycomp ...
- [题解]UVA10269 Adventure of Super Mario
链接:http://vjudge.net/problem/viewProblem.action?id=24902 描述:由城镇.村子和双向边组成的图,从A+B走到1,要求最短路.有K次瞬移的机会,距离 ...
- BI驾驶舱是什么?BI管理驾驶舱主要内容及特点
BI驾驶舱,顾名思义就是商业智能中让企业管理者对企业的管理能够找到在飞机或汽车驾驶舱里面的驾驶感觉.BI管理驾驶舱系统是专为企业管理层设计的BI分析系统,,是为企业高层打造的虚拟办公场景,有利于更好地 ...
- Linux添加永久路由的方法
通常我们使用route add -net添加临时路由.当系统重启,临时路由将丢失,重新配置路由带来了不必要的麻烦.可通过固化临时路由为永久路由的方法解决该问题. static-routes文件为路由固 ...
- Git——版本控制器概述
一.版本控制 版本控制(Revision contontrol)是一种在开发过程中用于管理修改历史,方便查看更改历史记录,备份以便恢复以前版本的软件工程的技术. 1.实现跨区域多人协同开发 2.追踪和 ...
- spring 中<ref parent="">标签是什么意思;ref标签与ref属性有什么不同;子容器如何引用父容器的bean
spring的配置文件可能会有多个<property name="a" ref="b" />就是找当前配置文件里的bean 也就是id为b的 < ...
- 在win10操作系统中pycharm启动时无法打开的解决方法
''' 当打开pycharm时报错 Error launching Pycharm Failed to load JVM DLL C:\Program Files\Jetbrains\Pycharm ...
- pandas模块篇(之二)
今日内容概要 布尔选择器 索引 数据对齐 数据操作(增出改查) 算术方法 DataFrame(Excel表格数据) 布尔选择器 import numpy as np import pandas as ...