【BZOJ3326】数数 数学题

来源:互联网 发布:开淘宝微店能赚钱吗 编辑:程序博客网 时间:2024/06/06 20:38

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 100000using 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
原创粉丝点击