zoukankan      html  css  js  c++  java
  • 【BZOJ】1833 [ZJOI2010]count 数字计数

    【算法】数位DP

    【题解】

    记忆化搜索

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    using namespace std;
    ll A[10],B[10],f[20][11],a[20],p[10];
    ll dfs(ll* A,ll h,bool limit,bool pre)
    {
        if(h==0)return 1;
        if(!limit&&f[h][10]!=-1&&!pre)
         {
             for(int i=0;i<=9;i++)A[i]+=f[h][i];
             return f[h][10];
         }
        int end=limit?a[h]:9;ll ans=0;
        for(int i=0;i<=end;i++)
         {
             if(limit&&i==end)p[i]=dfs(A,h-1,1,pre&(!i)),A[i]+=p[i],ans+=p[i];
              else p[i]=dfs(A,h-1,0,pre&(!i)),A[i]+=p[i],ans+=p[i];
             if(pre&(!i))A[i]-=p[i];
         }
        if(!limit&&!pre)
         {
             for(int i=0;i<=9;i++)f[h][i]=f[h-1][i]*10+p[i];
             f[h][10]=ans;
         }
        return ans;
    }
    void solve(ll* A,ll x)
    {
        if(x==0){return;}
        int n=0;
        while(x>0)a[++n]=x%10,x/=10;
        dfs(A,n,1,1);
    }
    int main()
    {
        ll l,r;
        scanf("%lld%lld",&l,&r);
        for(int i=0;i<=20;i++)f[i][10]=-1;
        solve(B,l-1);solve(A,r);
        for(int i=0;i<9;i++)printf("%lld ",A[i]-B[i]);
        printf("%lld
    ",A[9]-B[9]);
        return 0;
    }
    View Code

    递推:下面只考虑单一数字数量统计,其它一样。

    第一步,预处理。(计数)

    规定最低位为第1位,最高位为第len位。

    f[i][j]表示前i位,最高位数字为j的答案数(不考虑前导零有前导零的数字只要待会再最高位附上数字就是有效的了)

    f[i][j]=∑f[i-1][k]+10^(i-1)。

    第二步,第len位为0。(前导零)

    将所有len位为0的或数字长度不足len的数字先统计进来,ans+=∑f[i][j]+1,1<=i<=len-1,1<=j<=9。其中+1是数字0。

    第三步,第len位不为0逐位确定。(限位)

    强制确定len位不为0,然后加入每一位枚举到顶-1的答案就可以了。

    ans+=∑f[i][j],1<=i<len,0<=j<=a[i],其中i=len时j从1开始。

    还要计算当前数位的顶之后会出现的次数,为后面数字大小+1。

    例如1211这个数字,整个数位DP的过程是:0,1~9,10~99,100~999,1000~1199,1200~1209,1210~1211。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=16,M=20;
    int a[M];
    ll fac[M];
    struct cyc{
        ll a[10];
    }f[N][N];
    cyc operator + (cyc a,cyc b){
        cyc c;
        for(int i=0;i<=9;i++)c.a[i]=a.a[i]+b.a[i];
        return c;
    }
    cyc dp(ll num){
        int len=0;
        cyc ans;
        for(int i=0;i<=9;i++)ans.a[i]=0;
        ans.a[0]=1;
        if(!num)return ans;
        ll number=num;
        while(num){
            a[++len]=num%10;
            num/=10;
        }
        for(int i=1;i<len;i++)for(int j=1;j<=9;j++)ans=ans+f[i][j];
        for(int i=len;i>=1;i--){
            for(int j=(i==len);j<a[i];j++){
                ans=ans+f[i][j];
            }
            number%=fac[i-1];
            ans.a[a[i]]+=number+1;
        }
        //for(int k=0;k<=9;k++)printf("%d ",ans.a[k]);puts("");
        return ans;
    }
    int main(){
        fac[0]=1;for(int i=1;i<=N;i++)fac[i]=fac[i-1]*10;
        for(int j=0;j<=9;j++)f[1][j].a[j]=1;
        for(int i=2;i<=N;i++){
            for(int j=0;j<=9;j++){
                for(int k=0;k<=9;k++){
                    f[i][j]=f[i][j]+f[i-1][k];
                    f[i][j].a[j]+=fac[i-2];
                }//printf("[%d][%d]",i,j);
                //for(int k=0;k<=9;k++)printf("%d ",f[i][j].a[k]);puts("");
            }
        }
        ll A,B;scanf("%lld%lld",&A,&B);
        cyc cA=dp(A-1),cB=dp(B);
        for(int i=0;i<9;i++)printf("%lld ",cB.a[i]-cA.a[i]);
        printf("%lld",cB.a[9]-cA.a[9]);
        return 0;
    }
    View Code
  • 相关阅读:
    JPA的一对一映射
    JPA的查询语言—JPQL的命名查询@NamedQuery
    JPA的persistence.xml文件
    JPA的主键产生策略
    JPA的查询语言—JPQL的简单查询
    Oracle Flashback救急了一回
    JPA的实体
    JPA的一对多映射(双向)
    JPA的一对多映射(单向)
    MySQL 主从同步操作文档
  • 原文地址:https://www.cnblogs.com/onioncyc/p/6708708.html
Copyright © 2011-2022 走看看