Soratosorato

CF582D Number of Binominal Coefficients 题解

Sorato·2024-09-30 10:48·8 次阅读

CF582D Number of Binominal Coefficients 题解

CF582D Number of Binominal Coefficients 题解

纪念一下自己第一道独立 A 掉的黑题 / CF3300。

题目大意#

给定质数 p 和整数 α,A,求满足 0knApα|(nk) 的数对 (n,k) 的个数。

Solve#

首先,我们引入 Kummer定理,即:

p 在组合数 (nm) 中的幂次,恰好为 nmp 进制减法的借位次数。

所以我们只需统计 nkp 进制减法时借位次数大于等于 α(n,k) 的个数即可。

Step 1#

Ap 进制下数位分离,记其第 i 位为 ai

我们考虑如下常规数位 dp:设 f(i,j,0/1,0/1) 表示:前 i+1 位,借位次数为 jn 是否卡满了 A 的上界,k 是否卡满了 n 的上界,这种状态的 (n,k) 的个数。

但是,我们这一位是否借位,是和下一位(更小的那一位)是否借位有关的,如果下一位借位了,那么我们这一位相等时也可以借位,所以考虑多加一维,变为:f(i,j,0/1,0/1,0/1) 表示:前 i+1 位,借位次数为 jn 是否卡满了 A 的上界,k 是否卡满了 n 的上界,钦定这一位借位 / 不借位,这种状态的 (n,k) 的个数。那么我们有如下朴素的转移:

枚举第 i 位的 nikj,有:

f(i1,j,f1[i<ai],f2[j<i],f3)f(i,j,f1,f2,0),if3j0f(i1,j+1,f1[i<ai],f2[j<i],f3)f(i,j,f1,f2,1),if3j<0

f1,f2,f3 为枚举状态,常规的 f1,f2i,j 的限制就不再讨论了,要注意的是 f3,即是否钦定下一位借位,的限制。

时间复杂度约为 O(p2logpA),但本题 p109,考虑优化。

Step 2#

上面的转移中,j 的作用不是很大,所以我们只需枚举始状态 f1,f2,f3,再枚举这一位上是否借位(y),j<i 是否成立(g2),下一位是否借位(g3),受算一下即可求出相应状态下合法的 j 的取值范围 [l,r],那么我们有:

f(i1,j+y,f1[i<ai],f2g2,g3)(rl+1)f(i,j,f1,f2,f3).

lr 是好确定的,简单写一下吧,为后面化简做准备。

l=max{0i+1g3f3=1y=1ig2=0r=min{p1ig3f2=0f3=0y=0i1g2=1

时间复杂度约为 O(plogpA)。考虑更深一步讨论,尽量省去 i 和一些参数的枚举。

Step 3#

一步一步来,对参数分别讨论。上面的式子中,我们需保证 lr,所以据此讨论。

容易发现,当 f3=1 时,f2 也必须等于 1,因为若 f3=1,f2=0,那么 li+1g3,rig3l>r

同样的,我们有,y=f3,也是对 lr 的第二行式子讨论可得。

并且,当 f3=1 时,g2 只能是 0,因为若 g2=1,那么 r=i1<l=i+1g3

由此,我们有了更优美的转移式:

f(i1,j+1,f1[i<ai],1,g3)(rl+1)f(i,j,f1,1,1).f(i1,j,f1[i<ai],g2,g3)(rl+1)f(i,j,f1,0,0).f(i1,j,f1[i<ai],1,g3)(rl+1)f(i,j,f1,1,0).[l,r]={[i+1g3,p1]f3=1g2=0[i,ig3]f3=0g2=0[0,i1]f3=0g2=1

考虑把 l,r 的讨论转化为 rl+1,即系数的讨论,简单化简得:

(1)f(i1,j+1,f1[i<ai],1,g3)(p1i+g3)f(i,j,f1,1,1)(2){f(i1,j+1,f1[i<ai],1,0)(p1i)f(i,j,f1,1,1)f(i1,j+1,f1[i<ai],1,1)(pi)f(i,j,f1,1,1)(3)(4)f(i1,j,f1[i<ai],0,g3)(1g3)f(i,j,f1,0,0)(5)f(i1,j,f1[i<ai],0,0)f(i,j,f1,0,0)(6)(7)f(i1,j,f1[i<ai],1,g3)if(i,j,f1,0,0)(8){f(i1,j,f1[i<ai],1,0)if(i,j,f1,0,0)f(i1,j,f1[i<ai],1,1)if(i,j,f1,0,0)(9)(10)f(i1,j,f1[i<ai],1,g3)(i+1g3)f(i,j,f1,1,0)(11){f(i1,j,f1[i<ai],1,0)(i+1)f(i,j,f1,1,0)f(i1,j,f1[i<ai],1,1)if(i,j,f1,1,0)

时间复杂度仍约为 O(plogpA),只是优化了一些参数的枚举,枚举 i 的瓶颈仍未拿下。

Step 4#

化简之后,可以发现第四维的枚举用处不大,所以我们令 f(i,j,f1,f2)=f(i,j,f1,0,f2)+f(i,j,f1,1,f2),有:

(12){f(i1,j+1,f1[i<ai],0)(p1i)f(i,j,f1,1)f(i1,j+1,f1[i<ai],1)(pi)f(i,j,f1,1)(13){f(i1,j,f1[i<ai],0)(i+1)f(i,j,f1,0)f(i1,j,f1[i<ai],1)if(i,j,f1,0)

然后,对 i 的讨论就很明了了,分为 i<aiiai 两种情况即可,几个转移式的系数都是等差数列求和,很好算。

时间复杂度成功优化到约 O(logpA)

Code#

Copy
#include<bits/stdc++.h> using namespace std; inline int read() { char c=getchar(); int now=0;short f=1; while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9') now=(now<<1)+(now<<3)+(c^48),c=getchar(); return now*f; } const int N=3350,MOD=1e9+7,M=1010; using ll=long long; int p,c,f[2][N/*借位次数*/][2/*i=A*/][2/*这一位是否需要借位*/],n,a[N],ans; struct zzn//高精度封装,只需实现 除以低精 和 对低精取模,好写的 { int num[M],len; zzn(){len=0;memset(num,0,sizeof num);} inline void read() { char s[M];scanf("%s",s+1); len=strlen(s+1); for(int i=1;i<=len;i=-~i) num[i]=s[len-i+1]-'0'; } inline void print() { for(int i=len;i;i=~-i) printf("%d",num[i]); } zzn operator/(const int b)const { zzn res;res.len=len;ll r=0; for(int i=len;i;i=~-i) r=10ll*r+num[i],res.num[i]=r/b,r%=b; while(!res.num[res.len]&&res.len) res.len=~-res.len; return res; } int operator%(const int b)const { int res=0; for(int i=len;i;i=~-i) res=(10ll*res+num[i])%b; return res; } }A; signed main() { p=read();c=read();A.read(); while(A.len) a[n=-~n]=A%p,A=A/p; f[n&1][0][0][0]=1; for(int now=n;now;now=~-now) for(int x=0;x<=n-now;x=-~x) for(int f1=0;f1<2;f1=-~f1) { int t0=f[now&1][x][f1][0],t1=f[now&1][x][f1][1]; f[now&1][x][f1][0]=f[now&1][x][f1][1]=0; int i0=(f1?p-1:a[now]);//i枚举上界 //对于i<a[now] (f[now&1^1][x][1][1]+=1ll*(a[now]-1)*a[now]/2%MOD*t0%MOD)%=MOD, (f[now&1^1][x][1][0]+=1ll*(a[now]+1)*a[now]/2ll%MOD*t0%MOD)%=MOD; (f[now&1^1][x+1][1][1]+=1ll*(p*2-a[now]+1)*a[now]/2%MOD*t1%MOD)%=MOD, (f[now&1^1][x+1][1][0]+=1ll*(p*2-a[now]-1)*a[now]/2%MOD*t1%MOD)%=MOD; //对于i>=a[now] (f[now&1^1][x][f1][1]+=1ll*(a[now]+i0)*(i0-a[now]+1)/2%MOD*t0%MOD)%=MOD, (f[now&1^1][x][f1][0]+=1ll*(a[now]+i0+2)*(i0-a[now]+1)/2%MOD*t0%MOD)%=MOD; (f[now&1^1][x+1][f1][1]+=1ll*(p*2-a[now]-i0)*(i0-a[now]+1)/2%MOD*t1%MOD)%=MOD, (f[now&1^1][x+1][f1][0]+=1ll*(p*2-a[now]-i0-2)*(i0-a[now]+1)/2%MOD*t1%MOD)%=MOD; //下为暴力枚举 i 的转移 // for(int i=0;i<=i0;i=-~i) // { // (f[now&1^1][x+f2][f1|(i<a[now])][1]+=1ll*i*t0%MOD)%=MOD, // (f[now&1^1][x+f2][f1|(i<a[now])][0]+=1ll*(i+1)*t0%MOD)%=MOD; // (f[now&1^1][x+f2][f1|(i<a[now])][1]+=1ll*(p-i)*t1%MOD)%=MOD, // (f[now&1^1][x+f2][f1|(i<a[now])][0]+=1ll*(p-i-1)*t1%MOD)%=MOD; // } } for(int i=c;i<=n;i=-~i) for(int j=0;j<2;j=-~j) (ans+=f[0][i][j][0])%=MOD; return printf("%d",ans),0; }

再附上不同 Step 的代码。

  • Step 1O(p2logpA)
  • Step 2O(plogpA)
  • Step 3O(plogpA)
posted @   Sorato  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录