【BZOJ】3326: [Scoi2013]数数-数位DP

传送门:bzoj3326


题解

这题快把我写自闭了。。。
因为自己设的状态很少,一遍遍重写转移又把自己叉掉,(一道题做了一场比赛的时间。。。

F x F_x Fx表示 [ 1 , x ] [1,x] [1,x]的答案,则 a n s = F r − F l − 1 ans=F_r-F_{l-1} ans=FrFl1

对于 F x F_x Fx,从高位到低位处理,设 f i f_i fi表示处理到第 i i i位, [ 1 , x ] [1,x] [1,x]中以第 i i i位结尾且不达到数的上界(前 i i i位存在某位严格小于原数)的子串个数。 g i , 0 / 1 g_{i,0/1} gi,0/1分别表示 [ 1 , x ] [1,x] [1,x]中以第 i i i位结尾且达到/不达到数的上界的子串总贡献(0表示达到,1表示不达到)

具体转移详见代码,有兴趣的可以评论里问我。

总之我已经严重自闭了。
数位DP一定要想清楚再写。。。


代码

#include<bits/stdc++.h>
#define mem(f) memset(f,0,sizeof(f))
using namespace std;
typedef long long ll;
const int N=1e5+100,mod=20130427;

int n,m,B,ans,suf[N],pw[N];
int a[N],b[N],f[N],g[N][2];

inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
inline int inc(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dec(int x,int y){x-=y;return x<0?x+mod:x;}

inline int S(int x){return ((ll)x*(x+1)>>1)%mod;}
inline int calc(int x,int n)
{return inc(dec(suf[n],(ll)suf[x]*pw[n-x]%mod),1);}
int cal(int *a,int n)
{
	mem(f);mem(g);suf[0]=0;int i,j,re=0,lim;
	for(i=1;i<=n;++i) suf[i]=inc((ll)suf[i-1]*B%mod,a[i]);
	for(i=1;i<=n;++i){
		lim=a[i];
		
		if(i>1) f[i]=inc((ll)f[i-1]*B%mod,(ll)(i-1)*lim%mod);//previous
		
		if(i>1) ad(f[i],(ll)dec(suf[i-1],1)*B%mod),ad(f[i],B-1);//new
		if(lim>0) ad(f[i],lim-(i==1));
		
		if(i>1) g[i][0]=inc((ll)g[i-1][0]*B%mod,(ll)(i-1)*lim%mod);//previous
		ad(g[i][0],lim);//new
		
		if(i>1){
		   g[i][1]=inc((ll)g[i-1][1]*B%mod*(ll)B%mod,(ll)f[i-1]*S(B-1)%mod);
		   ad(g[i][1],inc((ll)g[i-1][0]*B%mod*(ll)lim%mod,(ll)(i-1)*S(lim-1)%mod));
		}//previous
		if(i>1) ad(g[i][1],(ll)dec(suf[i-1],1)*S(B-1)%mod),ad(g[i][1],S(B-1));
		if(lim>0) ad(g[i][1],S(lim-1));//new
		
		ad(re,inc((ll)g[i][0]*calc(i,n)%mod,(ll)g[i][1]*pw[n-i]%mod));
	}
	return re;
}

int main(){
    int i,j,k;
    scanf("%d",&B);
	scanf("%d",&n);for(i=1;i<=n;++i) scanf("%d",&a[i]);
	scanf("%d",&m);for(i=1;i<=m;++i) scanf("%d",&b[i]);
	pw[0]=1;for(i=1;i<N;++i) pw[i]=(ll)pw[i-1]*B%mod;
	if(n>1 || (n==1 && a[1]!=0)){
	   reverse(a+1,a+n+1);
	   for(i=1;(!a[i]);++i);
	   a[i]--;for(--i;i>0;--i) a[i]=B-1;// not 9
	   for(;(!a[n])&& n>1;--n);
	   reverse(a+1,a+n+1);
	}
	printf("%d",dec(cal(b,m),cal(a,n)));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值