zoukankan      html  css  js  c++  java
  • BZOJ 3326 [SCOI2013]数数 (数位DP)

    洛谷传送门

    题目:

    Fish 是一条生活在海里的鱼,有一天他很无聊,就开始数数玩。他数数玩的具体规则是:

    1. 确定数数的进制$B$

    2. 确定一个数数的区间$[L, R]$

    3. 对于$[L, R] $间的每一个数,把该数视为一个字符串,列出该字符串的每一个(连续的)子串对应的$B$进制数的值。

    4. 对所有列出的数求和。现在Fish 数了一遍数,但是不确定自己的结果是否正确了。由于$[L, R] $较大,他没有多余精力去验证是否正确,你能写一个程序来帮他验证吗?

    非常恶心的一道数位$DP$

    首先是数位$DP$的常规套路,用$[1,R]$的答案减去$[1,L-1]$的答案

    对于一个$B$进制数$S$,令$f_{S}$表示$S$所有后缀串所表示数的和,$l_{S}$表示数$S$的位数,现在在它末尾填上一个数$x$,则$F_{Sx}=F_{S}*B+x*(l_{S}+1)$

    令$g_{S}$表示$S$所有子串所表示数的和,则$g_{Sx}=g_{S}*B+F_{Sx}$

    我们要对$[1,S]$里的所有数进行统计,令$F_{i,0}$表示从高到低遍历到了第i位,未达到上限的所有数的$f_{x}$,$F_{i,1}$是达到上限的

    可得$F_{i+1,0}=sum_{x=1}^{B}(F_{i,0}*B+x*sum (l_{S}+1))=B^{2}F_{i,0}+frac{B(B-1)}{2}sum (l_{S}+1)$

    而$F_{i,1}$转移到$F_{i+1,0}$的也是类似的

    显然我们还要维护一个数组$L_{i}$,表示前i位数中出现的数的$l_{x}$之和

    因为要加上$sum (l_{S}+1)$,还需要维护一个$Sum_{i}$,表示前i位数中出现的数的数量

    这两个数组都很好维护

    最后就是统计答案了,令$G_{i,0}$表示从高到低遍历到了第i位,未达到上限的所有数的$g_{x}$之和,$G_{i,1}$是达到上限的

    因为每个$G_{i,0}$都有$B$次被转移,所以$G_{i+1,0}=Bcdot G_{i,0}+F_{i+1,0}$

    而$G_{i,1}$转移到$G_{i+1,0}$的情况也是类似的

    注意前导零的处理,我的方法是每遍历到新的一位,1~B每个数都还会作为一个新数的开头(除了第一位),把都加入状态里即可

     1 #include <cmath>
     2 #include <queue>
     3 #include <vector>
     4 #include <cstdio>
     5 #include <cstring>
     6 #include <algorithm>
     7 #define N1 101000
     8 #define N2 4201
     9 #define M1 120
    10 #define ll long long
    11 #define dd double  
    12 #define uint unsigned int
    13 #define idx(X) (X-'0')
    14 using namespace std;
    15 
    16 const int mod=20130427;
    17 int gint()
    18 {
    19     int ret=0,fh=1;char c=getchar();
    20     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    21     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
    22     return ret*fh;
    23 }
    24 int n,m,B;
    25 int f[N1][2],g[N1][2],s[N1][2],l[N1][2]; 
    26 ll solve(int *a,int len)
    27 {
    28     memset(f,0,sizeof(f));
    29     memset(g,0,sizeof(g));
    30     memset(l,0,sizeof(l));
    31     memset(s,0,sizeof(s));
    32     ll ans=0;
    33     s[1][0]=a[1]-1,s[1][1]=1;
    34     l[1][0]=a[1]-1,l[1][1]=1;
    35     f[1][0]=1ll*a[1]*(a[1]-1)/2%mod;
    36     f[1][1]=a[1];
    37     g[1][0]=f[1][0],g[1][1]=f[1][1];
    38     for(int i=1;i<len;i++)
    39     {
    40         s[i+1][0]=(1ll*s[i][0]*B%mod + 1ll*s[i][1]*a[i+1]%mod + B-1)%mod;
    41         s[i+1][1]=s[i][1];
    42         l[i+1][0]=(1ll*(l[i][0]+s[i][0])*B%mod + 1ll*(l[i][1]+s[i][1])*a[i+1]%mod + B-1)%mod;
    43         l[i+1][1]=(l[i][1]+s[i][1]);
    44         f[i+1][0]=(1ll*f[i][0]*B%mod*B%mod + 1ll*f[i][1]*a[i+1]%mod*B%mod + 1ll*(1ll*B*(B-1)/2%mod)*(l[i][0]+s[i][0]+1)%mod + 1ll*(1ll*a[i+1]*(a[i+1]-1)/2%mod)*(l[i][1]+s[i][1])%mod)%mod;
    45         f[i+1][1]=(1ll*f[i][1]*B%mod + 1ll*a[i+1]*(l[i][1]+1)%mod)%mod;
    46         g[i+1][0]=(1ll*g[i][0]*B%mod + 1ll*g[i][1]*a[i+1]%mod + f[i+1][0])%mod;
    47         g[i+1][1]=(g[i][1] + f[i+1][1])%mod;
    48     }
    49     return (g[len][0]+g[len][1])%mod;
    50 }
    51 int a[N1],b[N1],tmp[N1];
    52 
    53 
    54 int main()
    55 {
    56     scanf("%d",&B);
    57     scanf("%d",&n);
    58     for(int i=n;i>=1;i--)
    59         tmp[i]=gint();
    60     scanf("%d",&m);
    61     for(int i=1;i<=m;i++)   
    62         b[i]=gint();
    63     tmp[1]--;int k=1;
    64     while(tmp[k]<0) 
    65         tmp[k]+=B,tmp[k+1]--,k++;
    66     if(tmp[n]==0) n--;
    67     for(int i=1;i<=n;i++)
    68         a[i]=tmp[n-i+1];
    69     ll ans1=solve(a,n);
    70     ll ans2=solve(b,m);
    71     printf("%lld
    ",((ans2-ans1)%mod+mod)%mod);
    72     return 0;
    73 }
  • 相关阅读:
    JS函数防抖与函数节流
    AJAX问题 XMLHttpRequest.status = 0是什么含义
    通过JS如何获取IP地址
    关于URL编码
    报错Unexpected token u
    css文本超出2行就隐藏并且显示省略号
    At_speed_test
    Logic Bist Arch
    Logic BIST
    DMA-330(二)
  • 原文地址:https://www.cnblogs.com/guapisolo/p/10043806.html
Copyright © 2011-2022 走看看