NEUQ OJ 2004:追梦之人 (计数数位dp)

2004: 追梦之人

描述

题目描述:

为了纪念追梦人,粉丝们创造了一种新的数——“追梦数”。追梦数要满足以下两个条件:
1、数字中不能出现“7”
2、不能被7整除。
比如:777和4396就不是追梦数,而666是追梦数。现在他们想知道,1到N中有多少个追梦数。

输入:

多组数据。
第一行给出一个正整数T。T为数据组数。
接下来T行,每行包括一个正整数N。
(1 \leq T \leq 10001T1000
(1 \leq N \leq 10^{18}1N1018)

输出:

对于每组数据,在单独的一行中输出一个整数表示1到N中有多少个追梦数。

样例输入
4
10
14
17
100
样例输出
9
12
14
70

当初石乐志要维护三个东西,后来发觉数位只要维护两个东西就好了。
我们的答案是n-被7整除的数-不被7整除但包含7的数。
被7整除的数n/7即可求出。
后面这个写个数位dp即可。
dpi[k]表示长度为i,膜7的余数为j,是否含7的情况为k(k为0表示不含,为1表示含)的数的数量,考虑当前的第i位为x,之前的i-1位组成的数膜7的余数为y,那么dpi+=dpi-1。如果x不是7,那么k应该是0转移到0,1转移到1;如果x是7,那么都转移到k=1。剩下的就是裸的数位dp了。
注意边界。

 1 #include<bits/stdc++.h>
 2 #define clr(x) memset(x,0,sizeof(x))
 3 #define clr_1(x) memset(x,-1,sizeof(x))
 4 #define mod 7
 5 #define LL long long
 6 #define INF 0x3f3f3f3f
 7 #define mp make_pair
 8 #define pb push_back
 9 #define mp make_pair
10 #define fi first
11 #define se second
12 using namespace std;
13 const int N=1e5+10;
14 LL all[30][10][2],num[30];
15 void init()
16 {
17     all[0][0][0]=1;
18     num[1]=1;
19     for(int i=2;i<=18;i++)
20         num[i]=(num[i-1]*10)%mod;
21     for(int i=1;i<=18;i++)
22         for(int j=0;j<=9;j++)
23             for(int k=0;k<7;k++)
24                 if(j!=7)
25                 {
26                     all[i][(int)(j*num[i]%mod+k)%mod][1]+=all[i-1][k][1];
27                     all[i][(int)(j*num[i]%mod+k)%mod][0]+=all[i-1][k][0];
28                 }
29                 else
30                      all[i][(int)(j*num[i]%mod+k)%mod][1]+=all[i-1][k][1]+all[i-1][k][0];
31 }
32 LL n,m,k;
33 LL ans;
34 int a[N],t,now;
35 bool flag;
36 int T;
37 int main()
38 {
39     init();
40     scanf("%d",&T);
41     while(T--)
42     {
43         scanf("%lld",&n);
44         ans=n-n/7;
45         m=0;
46         n++;
47         while(n)
48         {
49             a[++m]=n%10;
50             n/=10;
51         }
52         now=0;
53         flag=0;
54         for(int i=m;i>=1;i--)
55         {
56             for(int j=0;j<a[i];j++)
57             {
58                 for(int k=0;k<mod;k++)
59                     if((j*num[i]%mod+k+now)%mod!=0)
60                     {
61                         ans-=all[i-1][k][1];
62                         if(flag || j==mod)
63                             ans-=all[i-1][k][0];
64                     }
65             }
66             now=(now+a[i]*num[i])%mod;
67             if(a[i]==mod)
68                 flag=1;
69         }
70         printf("%lld\n",ans);
71     }
72 }
View Code

 

posted @ 2018-05-14 04:33  hk_lin  阅读(341)  评论(0编辑  收藏  举报