6640. 【GDOI20205.20模拟】Lowbit

题目

这里的\(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……

猜你喜欢

转载自www.cnblogs.com/jz-597/p/12950983.html
今日推荐