题目
这里的\(lowbit(x)\)是\(p\)进制下的定义。
对于某个数\(x\),一次操作之后它会有\(\frac{a}{b}\)的概率变成\(x+lowbit(x)\),或者\(1-\frac{a}{b}\)的概率变成\(x-lowbit(x)\)
一个数的贡献是它变成\(0\)的期望步数。
求\([L,R]\)的贡献和。
\(p\leq 1e5\)
\(L\leq R \leq 1e18\)
结果对\(998244353\)取模。
思考历程
为了方便描述,记\(q=\frac{a}{b}\)
比较有用的性质:记\(f(x)\)为\(x\)的贡献。
如果\(p|x\),则\(f(x)=f(\frac{x}{p})\)
证明显然。
于是对\(p=2\)的情况直接递归暴力,就有了\(40\)分。
其实递归暴力的时间复杂度是有保障的,因为递归的时候遇到的不同的\(x\)不超过\(2\log_p x\)个。
考虑对一个\(x\)进行操作,它的个位要么会变成\(0\),要么变成\(p\)之后进一位。在那之后这个个位就可以去掉了。
于是对于所有\(x\in [0,p]\),计算\(x\)变成\(0\)的概率,变成\(p\)的概率,以及\(x\)变成\(0\)或\(p\)的期望步数。
(这里分别记作\(t0(x),tp(x),w(x)\))
举\(w(x)\)作为例子:
一个朴素的思路,就是对\(w(x)\)列出个递推式,然后进行无限次迭代。显然最终会收敛。
\(w(x)=(1-q)*w(x-1)+q*w(x+1)+1,x\in[1,p-1]\)
\(w(0)=0,w(q)=0\)
可以将\(w(x)\)表示成\(a_x*w(x+1)+b_x\)的形式。
假如已经求出了\(x\in [0,i-1]\)的\(a_x\)和\(b_x\),现在要求\(a_i\)和\(b_i\)
\(w(i)=(1-q)*w(i-1)+q*w(i+1)=(1-q)*(a_{i-1}*w(i)+b_{i-1})+q*w(i+1)\)
移项可得\(a_i\)和\(b_i\)。
由于\(w(p)\)已知,于是从后往前推,就可以求出所有的\(w(x)\)具体的值。
\(t0\)和\(tp\)也是类似的求法,列出一个迭代式,然后移项搞一搞。
然鹅我就是不会将算一段区间的贡献和。
正解
%%%DYP
设\(dp_{i,0/1,0/1}\)表示处理完了前\(i\)位,并且这\(i\)位都已经清\(0\),\(i+1\)位没有确定时范围内的数字到达这个状态的概率和。第二维表示是否受到边界的限制(受到边界的限制意思就是必须要小于等于边界的后缀),第三维表示是否有进位。
考虑转移,现在要求\(dp_{i,0/1,0/1}\),
对于\(dp_{i,0,0/1}\),从\(dp_{i-1,0,0/1}\)转移过来,枚举第\(i\)位原来的取值,用来决定转移的概率,以及转移之后是否进位。
对于\(dp_{i,1,0/1}\)的转移, 从\(dp_{i-1,0/1,0/1}\)转移过来,大体上差不多,不过还要考虑是否超界。具体来说,就是第\(i\)位的取值不能高于边界的对应位置上的值。在处理这个东西的同时,顺便将期望加到答案中。
具体见程序。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define P 100010
#define ll long long
#define mo 998244353
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
ll p,q,L,R;
ll pw[70];
ll t0[P],tp[P],w[P];
ll a[P],b[P];//ans(i)=ans(i+1)*a[i]+b[i]
ll dp[70][2][2];
ll calc(ll n){
memset(dp,0,sizeof dp);
ll res=0;
int x=n%p;
for (int j=0;j<p;++j){
(dp[0][0][0]+=t0[j])%=mo;
(dp[0][0][1]+=tp[j])%=mo;
(res+=(n/p)%mo*w[j]%mo)%=mo;
}
for (int j=0;j<=x;++j){
(dp[0][1][0]+=t0[j])%=mo;
(dp[0][1][1]+=tp[j])%=mo;
(res+=w[j])%=mo;
}
int i=1;
for (;pw[i]<=n;++i){
int x=n/pw[i]%p;
// printf("%d\n",x);
ll tmp=n/pw[i+1]%mo;
for (int j=0;j<p;++j){
(dp[i][0][0]+=t0[j]*dp[i-1][0][0]+t0[j+1]*dp[i-1][0][1])%=mo;
(dp[i][0][1]+=tp[j]*dp[i-1][0][0]+tp[j+1]*dp[i-1][0][1])%=mo;
(res+=(w[j]*dp[i-1][0][0]+w[j+1]*dp[i-1][0][1])%mo*tmp)%=mo;
}
for (int j=0;j<x;++j){
(dp[i][1][0]+=t0[j]*dp[i-1][0][0]+t0[j+1]*dp[i-1][0][1])%=mo;
(dp[i][1][1]+=tp[j]*dp[i-1][0][0]+tp[j+1]*dp[i-1][0][1])%=mo;
(res+=w[j]*dp[i-1][0][0]+w[j+1]*dp[i-1][0][1])%=mo;
}
(dp[i][1][0]+=t0[x]*dp[i-1][1][0]+t0[x+1]*dp[i-1][1][1])%=mo;
(dp[i][1][1]+=tp[x]*dp[i-1][1][0]+tp[x+1]*dp[i-1][1][1])%=mo;
(res+=w[x]*dp[i-1][1][0]+w[x+1]*dp[i-1][1][1])%=mo;
}
--i;
(res+=dp[i][1][1]*(w[1]*qpow(t0[1])%mo))%=mo;
return res;
}
int main(){
freopen("lowbit.in","r",stdin);
freopen("lowbit.out","w",stdout);
ll qa,qb;
scanf("%lld%lld%lld%lld%lld",&p,&qa,&qb,&L,&R);
pw[0]=1;
for (int i=0;pw[i]<=R;++i)
pw[i+1]=pw[i]*p;
q=qa*qpow(qb)%mo;
a[0]=0,b[0]=0;
for (int i=1;i<p;++i){
ll inv=qpow(((1-a[i-1]*(1-q))%mo+mo)%mo);
a[i]=q*inv%mo;
b[i]=(b[i-1]*(1-q+mo)%mo+1)*inv%mo;
}
w[p]=0;
for (int i=p-1;i>=0;--i)
w[i]=(a[i]*w[i+1]+b[i])%mo;
a[0]=0,b[0]=1;
for (int i=1;i<p;++i){
ll inv=qpow(((1-a[i-1]*(1-q))%mo+mo)%mo);
a[i]=(q+mo)*inv%mo;
b[i]=b[i-1]*(1-q+mo)%mo*inv%mo;
}
t0[p]=0,tp[p]=1;
for (int i=p-1;i>=0;--i){
t0[i]=(a[i]*t0[i+1]+b[i])%mo;
tp[i]=(1-t0[i]+mo)%mo;
}
printf("%lld\n",(calc(R)-calc(L-1)+mo)%mo);
return 0;
}
总结
数位DP,最恶心的DP……