bzoj3326: [Scoi2013]数数(数位dp)

博客介绍了Scoi2013数学竞赛中的一道数数题目,涉及数位动态规划。题目要求在指定进制[B]和数的区间[L, R]内,计算所有数的连续子串转换为B进制后的和,最后以10进制表示并模20130427。作者分享了解题思路,包括预处理不同情况下的子串和,以及如何进行状态转移。虽然开始从低位处理导致复杂性增加,但最终成功解决问题。" 127248613,15332058,Nacos集群搭建与持久化配置详解,"['Java开发', '分布式', '数据库管理', '集群', '配置管理']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
题意:
一个人数数,规则如下:

  1. 确定数数的进制B
  2. 确定一个数数的区间[L, R]
  3. 对于[L, R] 间的每一个数,把该数视为一个字符串,列出该字符串的所有连续子串对应的B进制数的值。
  4. 对所有列出的数求和。

结果用10 进制表示,对20130427取模。


思路:
我不知道为什么要从低位开始向高位处理2333333手动毒瘤
然后肝了好久幸好没有推错不然就自闭了
不过需要多预处理一点东西。
假设现在计算 [ 1 , a ] [1,a] [1,a]的答案, a a a一个表示 B B B进制数的数组
s s i : ss_i: ssi:所有不含前导 0 0 0 i i i位数的子串之和。
s 1 i : s_{1i}: s1i:所有包含前导 0 0 0 i i i位数的子串之和。
s 2 i : s_{2i}: s2i:所有包含前导 0 0 0 i i i位数的前缀串之和。
p w i : pw_i: pwi: B B B i i i次方。
s p w i : spw_i: spwi: p w pw pw的前缀和。
预处理出上面的信息就可以递推了(注意我是从后往前推的所以很复杂):
设现在在第 i i i位,我们记 f f f表示 i i i~ n n n满足 a a a数组限制的所有 n − i + 1 n-i+1 ni+1位数的子串之和, p f pf pf表示 i + 1 i+1 i+1位推出的 f f f;
g g g表示满足 a a a数组限制的所有 n − i + 1 n-i+1 ni+1位数的个数, p g pg pg表示 i + 1 i+1 i+1位推出的 g g g;
s s s表示满足 a a a数组限制的所有 n − i + 1 n-i+1 ni+1位数的前缀串之和, p s ps ps表示 i + 1 i+1 i+1位推出的 s s s
然后就可以分第 i i i位填 0 0 0,填 1 1 1~ a i − 1 a_i-1 ai1, a i a_i ai的情况大力转移了,注意特判 a i = 0 a_i=0 ai=0的情况。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
const int N=1e5+5,mod=20130427;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void update(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
int B,n,ans=0,a[N],pw[N],spw[N],s1[N],s2[N],ss[N];
inline int calc(int x){return (ll)(x+1)*x/2%mod;}
inline void init(){
	pw[0]=spw[0]=1;
	for(ri i=1;i<=n;++i)pw[i]=mul(pw[i-1],B),spw[i]=add(pw[i],spw[i-1]);
	s1[0]=s2[0]=ss[0]=0;
	for(ri i=1,tmp=calc(B-1);i<=n;++i)s2[i]=add(mul(tmp,mul(pw[i-1],spw[i-1])),mul(s2[i-1],B));
	for(ri i=1,tmp=calc(B-1);i<=n;++i)s1[i]=add(mul(tmp,mul(pw[i-1],spw[i-1])),mul(B,add(s2[i-1],s1[i-1])));
	for(ri i=1;i<=n;++i)ss[i]=add(mul(add(s1[i-1],s2[i-1]),B-1),mul(calc(B-1),mul(pw[i-1],spw[i-1]))),update(ss[i],ss[i-1]);
	update(ans,ss[n-1]);
}
inline void solve(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	init();
	for(ri tmp,ps=0,pf=0,pg=1,s,f,g,i=n;i;--i,pf=f,pg=g,ps=s){
		if(!a[i]){f=add(pf,ps),g=pg,s=ps;continue;}
		f=s=0,g=1;
		tmp=mul(a[i]-1,add(s1[n-i],s2[n-i]));
		update(tmp,mul(mul(calc(a[i]-1),spw[n-i]),pw[n-i]));
		update(f,tmp);
		if(i==1)update(ans,tmp);
		g=mul(g,mul(pw[n-i],a[i]-1));
		update(s,mul(mul(calc(a[i]-1),pw[n-i]),spw[n-i]));
		update(s,mul(s2[n-i],a[i]-1));
		update(f,add(s1[n-i],s2[n-i]));
		update(g,pw[n-i]);
		update(s,s2[n-i]);
		tmp=mul(mul(a[i],pg),spw[n-i]);
		update(tmp,pf),update(tmp,ps);
		update(f,tmp);
		if(i==1)update(ans,tmp);
		update(g,pg),update(s,mul(mul(pg,a[i]),spw[n-i])),update(s,ps);
	}
}
inline void Solve(){for(ri i=1,sum=0;i<=n;++i)ans=add(ans,sum=add(mul(sum,B),mul(a[i],i)));}
int main(){
	B=read();
	solve();
	ans=mod-ans;
	Solve();
	solve();
	cout<<ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值