3326: [Scoi2013]数数
Time Limit: 1 Sec Memory Limit: 64 MB
Description
Fish 是一条生活在海里的鱼,有一天他很无聊,就开始数数玩。
他数数玩的具体规则是:
1. 确定数数的进制B
2. 确定一个数数的区间[L, R]
3. 对于[L, R] 间的每一个数,把该数视为一个字符串,列出该字符串的每一个(连续的)子串对应的B进制数的值。
4. 对所有列出的数求和。
现在Fish 数了一遍数,但是不确定自己的结果是否正确了。由于[L, R] 较大,他没有多余精力去验证是否正确,你能写一个程序来帮他验证吗?
Input
输入包含三行。
第一行仅有一个数B,表示数数的进制。
第二行有N +1 个数,第一个数为N,表示数L 在B 进制下的长度为N,接下里的N个数从高位到低位的表示数L 的具体每一位。
第三行有M+ 1 个数,第一个数为M,表示数R 在B 进制下的长度为M,接下里的M个数从高位到低位的表示数R 的具体每一位。
20% 数据,0 <= R <= L <= 10^5。
50% 数据,2 <= B <= 1000,1 <= N,M <= 1000。
100% 数据,2 <= B <= 10^5,1 <= N,M <= 10^5。
Output
输出仅一行,即按照Fish 数数规则的结果,结果用10 进制表示,由于该数可能很大,输出该数模上20130427的模数。
Sample Input
10
3 1 0 3
3 1 0 3
Sample Output
120
Hint
[103, 103] 之间仅有数103,该数的所有子串包括1, 10, 103, 0, 03, 3,其和为120。
说是数位DP……乱推呗……老套路, f(i) 表示位数不超过i位的所有数(允许前导0,可以看作是由0~b-1组成的串)按照fish的方法数数得到的所有的数的和,递推时,新加了一位数后,我们分别考虑包含这一位的字串和不包含这一位的字串,而对于包含这一位的字串,我们又把它的最高位拆出来分别计算,这样的思考方式可以得到这样的一个递推式: f(i)=bf(i−1)+bi(bi−1)2+bi(∑j=0i−1bj−i)2 ,具体推导过程略……总之稍有不慎就推错了……
更恶心的是后面的统计答案。
套路还是老样子……从高位向低位扫,处理完每位数字小于这位本身上限的情况,往后统计的时候认为这一位的数字等于上限……在处理某一位的时候,要考虑三个部分:只包含这一位之前的所有位中的某些位的串,包含这一位的字串,以及只包含这一位后面的某些位的串。同时,对于第一位还要特殊处理,因为题目是不允许前导0的……呐,具体实现就不说了,还是看看代码吧(博主推得吐血)……
#include
#include
#define maxn 100000
using namespace std;
typedef long long LL;
const LL p=20130427,inv2=(p+1)/2;
LL b,numl[maxn+10],numr[maxn+10],bp[maxn+10],f[maxn+10],h[maxn+10],m[maxn+10],s[maxn+10];
LL cal(int n,LL *num){
LL ret=0,r=num[1],pre=0,suf=0;
ret=(f[n-1]-h[n-1]+p+(r-1)*(2*f[n-1]%p+r*bp[n-1]%p*s[n-1]%p+2*m[n])%p*inv2%p)%p;
pre=suf=r;
for(int i=2;i<=n;i++){
r=num[i];
ret=(ret+r*f[n-i]%p+(r*b%p*suf%p+r*(r-1)/2%p*i%p)*bp[n-i]%p*s[n-i]%p+r*i%p*m[n-i+1]%p+pre*r%p*bp[n-i]%p)%p;
suf=(suf*b+i*r)%p;
pre=(pre+suf)%p;
}
return ret;
}
int main(){
int nl,nr;
scanf("%d",&b);
scanf("%d",&nl);
for(int i=1;i<=nl;i++)scanf("%lld",&numl[i]);
scanf("%d",&nr);
for(int i=1;i<=nr;i++)scanf("%lld",&numr[i]);
bp[0]=s[0]=1;
for(int i=1;i<=maxn+5;i++)bp[i]=bp[i-1]*b%p;
for(int i=1;i<=maxn+5;i++)s[i]=(b*s[i-1]+1)%p;
for(int i=1;i<=maxn+5;i++){
m[i]=bp[i-1]*(s[i-1]-i+p)%p*inv2%p;
f[i]=(b*f[i-1]%p+bp[i]*(bp[i]-1+p)%p*inv2%p+b*m[i]%p);
h[i]=(h[i-1]+m[i])%p;
}
//因为我那套路处理的是小于某个数的所有数,所以我们把上界暴力加一……
numr[nr]++;
for(int i=nr;i;i--)if(numr[i]==b){
numr[i]=0;
numr[i-1]++;
}else break;
if(numr[0]){
for(int i=nr;i>=0;i--)numr[i+1]=numr[i];
nr++;
}
cout<<(cal(nr,numr)-cal(nl,numl)+p)%p<return 0;
}
再附个暴力……方便验算= =
#include
#include
using namespace std;
typedef long long LL;
int num[20];
const LL p=20130427;
LL find(int x){
int n=0;
while(x){
num[++n]=x%10;
x/=10;
}
LL ret=0;
for(int i=n;i;i--){
LL t=0;
for(int j=i;j;j--){
t=(t*10+num[j])%p;
// printf("%d\n",t);
ret+=t;
ret%=p;
}
}
return ret;
}
int main(){
//freopen("test.out","w",stdout);
int a=339,b=2359;
LL ans=0;
for(int i=a;i<=b;i++)ans+=find(i);
cout<