Alice的幸运数
Alice的幸运数
题目背景
BJWC2013 T1
题目描述
在数学课上,Alice 知道了什么是最大公约数:
对于任意两个非零整数
x
x
x,
y
y
y,若整数
d
d
d 能同时被
x
x
x 和
y
y
y 整除,则称
d
d
d 为
x
x
x 与
y
y
y 的公约数。定义
x
x
x 与
y
y
y 的最大公约数
g
c
d
(
x
,
y
)
gcd(x,y)
gcd(x,y) 为
x
x
x 与
y
y
y 的最大的公约数。
如
g
c
d
(
6
,
9
)
=
3
gcd(6,9)=3
gcd(6,9)=3,
g
c
d
(
12
,
16
)
=
4
gcd(12,16)=4
gcd(12,16)=4,
g
c
d
(
25
,
32
)
=
1
gcd(25,32)=1
gcd(25,32)=1,等等。
继而 Alice 对这样一类数产生了兴趣,并称它们为幸运数:
对于一个正整数
d
d
d ,我们使用
d
i
d_i
di 表示
d
d
d 在十进制表示下,按从低位到高位顺序的第
i
i
i 位数字。
设
F
(
d
)
F(d)
F(d) 表示
d
d
d 的奇数位的数字之和,即
F
(
d
)
=
d
1
+
d
3
+
d
5
+
…
…
F(d)=d_1+d_3+d_5+……
F(d)=d1+d3+d5+……;
设
G
(
d
)
G(d)
G(d) 表示
d
d
d 的偶数位的数字之和,即
G
(
d
)
=
d
2
+
d
4
+
d
6
+
…
…
G(d)=d_2+d_4+d_6+……
G(d)=d2+d4+d6+……;
若
F
(
d
)
F(d)
F(d) 与
G
(
d
)
G(d)
G(d) 均大于 0 ,且
F
(
d
)
F(d)
F(d) 与
G
(
d
)
G(d)
G(d) 的最大公约数不超过
K
K
K ,则称
d
d
d 为幸运数。其中
K
K
K 是一个已知的常数。
举个例子来说,若
d
=
641
d=641
d=641,则
d
1
=
1
d_1=1
d1=1,
d
2
=
4
d_2=4
d2=4,
d
3
=
6
d_3=6
d3=6,
F
(
641
)
=
1
+
6
=
7
F(641)=1+6=7
F(641)=1+6=7,
G
(
641
)
=
4
G(641)=4
G(641)=4。此时
F
(
d
)
F(d)
F(d) 与
G
(
d
)
G(d)
G(d) 的最大公约数即
g
c
d
(
7
,
4
)
gcd(7,4)
gcd(7,4) 等于
1
1
1 。则当
K
K
K 不小于
1
1
1 时
641
641
641 是幸运数。
你需要帮助 Alice 解答下面的问题:
对于给定的
K
K
K ,在不小于
L
L
L 并且不超过
R
R
R 的所有整数中,有多少个数是幸运数?
注意,输入文件包含多组测试数据。
输入格式
第一行包含一个整数
T
T
T ,表示有
T
T
T 组测试数据。
接下来
T
T
T 行,每行包含三个整数
K
、
L
、
R
K、L、R
K、L、R ,表示一次询问。
输出格式
输出 T 行,每行一个整数,依次表示每组测试数据的答案。
样例数据
输入
5
1 1 10
2 28 34
100 987654321 987654321
1 1 50000
1 50001 100000
输出
0
5
1
30298
30309
备注
【样例解释】
K
=
1
K=1
K=1 时,
1
1
1 到
10
10
10 之间不存在幸运数。
K
=
2
K=2
K=2 时,
28
28
28 到
34
34
34 之间的幸运数有
28
、
29
、
31
、
32
、
34
28、29、31、32、34
28、29、31、32、34,共
5
5
5 个。
K
=
100
K=100
K=100 时,
987654321
987654321
987654321 是幸运数。
K
=
1
K=1
K=1 时,
1
1
1 到
50000
50000
50000 之间的幸运数有
30298
30298
30298 个 ,
50001
50001
50001 到
100000
100000
100000 之间的幸运数有
30309
30309
30309 个。
【数据规模和约定】
对于 10% 的数据:
1
≤
L
≤
R
≤
1
0
3
1≤L≤R≤10^3
1≤L≤R≤103。
另有 10% 的数据:
1
≤
L
≤
R
≤
1
0
7
,
1
≤
K
,
T
≤
10
1≤L≤R≤10^7,1≤K,T≤10
1≤L≤R≤107,1≤K,T≤10。
另有 10% 的数据:
1
≤
L
≤
R
≤
1
0
9
,
K
=
1
,
1
≤
T
≤
10
1≤L≤R≤10^9,K=1,1≤T≤10
1≤L≤R≤109,K=1,1≤T≤10。
对于 60% 的数据:
1
≤
L
≤
R
≤
1
0
1
2
,
1
≤
K
,
T
≤
100
1≤L≤R≤10^12,1≤K,T≤100
1≤L≤R≤1012,1≤K,T≤100。
对于 100% 的数据:
1
≤
L
≤
R
≤
1
0
1
8
,
1
≤
K
≤
100
,
1
≤
T
≤
1000
1≤L≤R≤10^18,1≤K≤100,1≤T≤1000
1≤L≤R≤1018,1≤K≤100,1≤T≤1000。
思路:
又双叒叕是万恶的数位dp,看来我要学一学了…
方法:预处理
D
P
[
i
]
[
k
]
[
s
0
]
[
s
1
]
DP[i][k][s_0][s_1]
DP[i][k][s0][s1]表示还有i个数位没有填写,已填写的与i同奇偶的数位和等于
s
0
s_0
s0,不同奇偶的数位和等于
s
1
s_1
s1,
K
=
k
K = k
K=k时的方案数。
转移方程:
D
P
[
i
]
[
k
]
[
s
0
]
[
s
1
]
=
∑
D
P
[
i
−
1
]
[
k
]
[
s
1
−
d
]
[
s
0
]
,
d
=
0...9
DP[i][k][s_0][s_1]=∑DP[i-1][k][s_1-d][s_0],d=0...9
DP[i][k][s0][s1]=∑DP[i−1][k][s1−d][s0],d=0...9
将<=N的数按与N的公共前缀长度分类!
举个例子,
N
N
N = 1234
分类:0xxx,10xx,11xx,120x,121x,122x,1230,1231,1232,1233,1234
这样,对于每个输入数据只需要枚举一遍所有类别的数
代码:
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
inline char ch() {
static char buf[1<<21],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}
inline int in {
int s=0,f=1;
char x;
for(x=ch(); x<'0'||x>'9'; x=ch()) if(x=='=') f=-1;
for( ; x>='0'&&x<='9'; x=ch()) s=(s<<1)+(s<<3)+(x&15);
return f==1?s:-s;
}
const int A=18;
const int B=82;
int dp[A][B][B][B];
int T;
int p,l,r;
int dit[A];
inline int gcd(int x,int y) {
return !y?x:gcd(y,x%y);
}
inline void prepare() {
for(int i=1; i<B; i++)
for(int j=1; j<B; j++)
for(int k=gcd(i,j); k<B; k++)
dp[0][k][i][j]=1;
for(int len=0; len<A-1; len++) {
int s=(19-len)/2*9;
for(int k=1; k<B; k++)
for(int i=0; i<=s; i++)
for(int j=0; j<=s; j++)
for(int d=0; d<=9&&d<=j; d++)
dp[len+1][k][j-d][i]+=dp[len][k][i][j];
}
return;
}
inline int work(int x) {
if(x<=10) return 0;
if(x==1e18) x--;
int res=0,len=0;
while(x) {
dit[len++]=x%10;
x/=10;
}
int a=0,b=0;
for(int i=len-1; i>=0; i--) {
for(int j=0; j<dit[i]; j++) {
if(i&1) res+=dp[i][p][b][a+j];
else res+=dp[i][p][a][b+j];
}
if(i&1) a+=dit[i];
else b+=dit[i];
}
res+=dp[0][p][a][b];
return res;
}
signed main() {
// freopen("lucky.in","r",stdin);
// freopen("lucky.out","w",stdout);
prepare();
T=in;
while(T--) {
p=in,l=in,r=in;
if(p>81) p=81;
int res=work(r)-work(l-1);
printf("%lld\n",res);
}
return 0;
}