zoukankan      html  css  js  c++  java
  • BZOJ2425:[HAOI2010]计数(数位DP)

    Description

    你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数。比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1020,等等。

    现在给定一个数,问在这个数之前有多少个数。(注意这个数不会有前导0).

    Input

    只有1行,为1个整数n.

    Output

    只有整数,表示N之前出现的数的个数。

    Sample Input

    1020

    Sample Output

    7

    HINT

    n的长度不超过50,答案不超过263-1.

    Solution

    感觉我的数位DP还不是很稳啊……
    全靠面向WA和面向样例来编程(逃
    首先这个题并不是很难,因为很容易想到:
    当某一位没有限制的时候,这一位以及后面的位数就可以用全排列来求数的个数了
    求全排列时要去重
    只不过数据太大所以求全排列的时候要分解质因数……否则只有60分
    啊啊啊好多细节烦死了

    Code

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<algorithm>
     5 #define LL long long
     6 using namespace std;
     7 LL num[1001],a[1001],cnt,pos,num_sum,Keg[1001];
     8 LL used[1001],prime[50]={0,2,3,5,7,9,11,13,17,19,23,29,31,37,41,43,47};
     9 char n[101];
    10 
    11 void divide(LL x,LL k)//将x分解质因数加入桶内 
    12 {
    13     for (LL i=1;i<=16;++i)
    14         while (x%prime[i]==0 && x)
    15         {
    16             Keg[i]+=k;
    17             x/=prime[i];
    18         }
    19 }
    20 
    21 LL check(LL x)
    22 {
    23     LL sum=1;
    24     memset(Keg,0,sizeof(Keg));//桶清空 
    25     for (int i=1;i<=x;++i) divide(i,1);//求全排列 
    26     for (LL i=1;i<=9;++i)//若一个数字出现多次就肯定会重复,重复个数为num[i]! 
    27     {
    28         for (int j=1;j<=num[i];++j) divide(j,-1);
    29         x-=num[i];
    30     }
    31     if (x<0) return 0;//这里的意思是,全排列的位数摆不开那些没有使用的非0数字 
    32     for (int i=1;i<=x;++i) divide(i,-1);//别忘了0也要去重 
    33     for (int i=1;i<=16;++i)
    34         for (int j=1;j<=Keg[i];++j)
    35             sum*=prime[i];
    36     return sum;
    37 }
    38 
    39 LL Dfs(LL pos,LL limit,LL used)
    40 {
    41     if (pos==0) return used==num_sum;//必须要所有非0数字用光才能返回1 
    42     if (!limit)
    43         return check(pos);//没有限制,直接求后面的全排列 
    44         
    45     LL ans=0,up=limit?a[pos]:9;
    46     for (LL i=0;i<=up;++i)
    47     {
    48         if (num[i]<=0 && i!=0) continue;
    49         num[i]--;
    50         ans+=Dfs(pos-1,limit && i==a[pos],used+(i!=0));
    51         num[i]++;
    52     }
    53     return ans;
    54 }
    55 
    56 int main()
    57 {
    58     scanf("%s",n);
    59     for (int i=strlen(n)-1;i>=0;--i)
    60     {
    61         if (n[i]!='0')
    62             num[n[i]-'0']++,num_sum++;
    63         a[++pos]=n[i]-'0';
    64     }
    65     printf("%lld",Dfs(pos,true,0)-1);
    66 }
  • 相关阅读:
    Android ListView 列表视图
    android handler msg的使用 实现进度条
    Intent 传递数据
    微服务-springcloud
    微服务-dubbo学习
    日志收集系统
    微服务追踪
    链表有环判断,快慢指针两种方法/合并链表/删除重复元素/二分递归和while
    算法练习,链表二分最大n个
    池以及barrier简单
  • 原文地址:https://www.cnblogs.com/refun/p/8680872.html
Copyright © 2011-2022 走看看