【BZOJ3326】数数 数学题

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(i1)+bi(bi1)2+bi(j=0i1bji)2 ,具体推导过程略……总之稍有不慎就推错了……
更恶心的是后面的统计答案。
套路还是老样子……从高位向低位扫,处理完每位数字小于这位本身上限的情况,往后统计的时候认为这一位的数字等于上限……在处理某一位的时候,要考虑三个部分:只包含这一位之前的所有位中的某些位的串,包含这一位的字串,以及只包含这一位后面的某些位的串。同时,对于第一位还要特殊处理,因为题目是不允许前导0的……呐,具体实现就不说了,还是看看代码吧(博主推得吐血)……

#include<cstdio>
#include<iostream>
#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<<endl;
    return 0;
}

再附个暴力……方便验算= =

#include<cstdio>
#include<iostream>
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<<ans%p<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值