第一题:abc048b B - Between a and b ...
问题描述
给定非负整数 a 和 b(a≤b),以及一个正整数 x。在 a 和 b 之间(包括 a 和 b)的整数中,有多少个可以被 x 整除?
约束条件
- 0≤a≤b≤1018 1≤x≤1018
输入
输入从标准输入以以下格式给出:
a b x
输出
打印在 a 和 b 之间(包括 a 和 b)的整数中,可以被 x 整除的数的个数。
样例输入 1
4 8 2
样例输出 1
3
在 4 和 8 之间(包括 4 和 8)的整数中,有三个可以被 2 整除的数:4、6 和 8。
样例输入 2
0 5 1
样例输出 2
6
在 0 和 5 之间(包括 0 和 5)的整数中,有六个可以被 1 整除的数:0、1、2、3、4 和 5。
样例输入 3
9 9 2
样例输出 3
0
在 9 和 9 之间(包括 9 和 9)的整数中,没有可以被 2 整除的数。
样例输入 4
1 1000000000000000000 3
样例输出 4
333333333333333333
注意整数溢出。
分析:直接暴力枚举,肯定会超时,那么我们可以思考:从左端点a包含x的个数到右端点b包含x的个数是单调递增的,那么我们只需要计算出左端点a包含x的个数和右端点b包含x的个数,然后做差就是a—b范围内整除x的个数,通过推导样例发现,左端点需要特殊处理一下,代码如下:
#include<bits/stdc++.h> using namespace std; long long a,b,x; int main() { cin>>a>>b>>x; if(a%x==0) cout<<b/x-a/x+1; else cout<<b/x-a/x; return 0; }
第二题:abc131cL. C - Anti-Division
题目描述
给定四个整数A,B,C和D。找出在A和B之间(包括A和B)不能被C和D整除的整数个数。
约束条件
- 1≤A≤B≤1018 1≤C,D≤109 输入中的所有值都是整数。
输入
从标准输入以以下格式给出输入:
A B C D
输出
打印在A和B之间(包括A和B)能被C和D整除的整数个数。
样例输入1
4 9 2 3
样例输出1
2
5和7满足条件。
样例输入2
10 40 6 8
样例输出2
23
样例输入3
314159265358979323 846264338327950288 419716939 937510582
样例输出3
532105071133627368
题目分析:本题暴力枚举显然也不可取
那么我们分析:要找出A和B之间不能被C、D整除的数的个数,不妨先找出能被C、D整除的数的个数,然后用总数做减法即可。接下来,我们可以分步骤进行:
1、 先找出A和B之间能被C整除的数的个数,这个问题的解决方法同上一题一模一样。
2、 再找出A和B之间能被D整除的数的个数,这个问题的解决方法同上一题一模一样。
但是我们通过样例1分析:4 9 2 3 4-9之间能被2整除的有4 6 8,4-9之间能被3整除的有6 9,他们有个重复数6,再举例比如4 13 2 3 4-13之间能被2整除的有4 6 8 10 12
4-13之间能被3整除的有6 12 ,他们有两个重复数6 12,那么6正好是2和3的最小公倍数,凡是最小公倍数的倍数的数都出现了重复,因此需要再求出来A和B之间能被C和D的最小公倍数整除的数的个数,去掉这些重复的就是答案~。
代码如下:
#include <bits/stdc++.h> using namespace std; int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); } int main() { long long a,b,c,d,ans1,ans2,ans3,gb,zs,nc; cin >> a >> b >> c >> d; gb=(c*d)/gcd(c,d);//求最小公倍数 if(a%c==0)//求a-b之间能被c整除的数的个数 ans1=b/c-a/c+1; else ans1=b/c-a/c; if(a%d==0)//求a-b之间能被d整除的数的个数 ans2=b/d-a/d+1; else ans2=b/d-a/d; if(a%gb==0)//求a-b之间能被最小公倍数整除的数的个数 ans3=b/gb-a/gb+1; else ans3=b/gb-a/gb; zs=b-a+1;//a-b之间所有的数 nc=ans1+ans2-ans3;//能被a,b整除的数 = 能被a整除的数 + 能被b整除的数 - 能被a和b的最小公倍数整除的数(重复的数),可通过样例推出结论 cout<<zs-nc;//不能被整除的数的个数 return 0; }
第三题:abc133cL. C - Remainder Minimization
题目描述
给定两个非负整数 L 和 R。我们将选择两个整数 i和 j,使得 L≤i<j≤R。求 (i×j) mod 2019 的最小可能值。
约束条件
- 输入的所有值都是整数。
- 0≤L<R≤2×109
输入
从标准输入以以下格式给出输入:
L R
输出
打印出在给定条件下选择 i和 j 时,(i×j) mod 2019的最小可能值。
样例输入 1
2020 2040
样例输出 1
2
当 (i,j)=(2020,2021) 时,(i×j) mod 2019=2。
样例输入 2
4 5
样例输出 2
20
我们只有一个选择:(i,j)=(4,5)。
题目分析:暴力枚举依然会超时
那么我们想,
思路1:如果L或R只要有一个能整除2019,那么肯定能找出i、j使得(i×j) mod 2019=0
思路2:计算出左端点L和右端点R分别包含2019的个数(此处就要用到第一题所讲的解题思路),如果左端点包含的个数小于右端点包含的个数(此处就要用到第一题所讲的解题思路),说明在L和R之间一定包含一个数能整除2019,同样可以找出i、j使得(i×j) mod 2019=0。
思路3:排除上述两种情况,那么需要枚举的区间就只剩下最多2019个数,所以暴力枚举所有情况,时间复杂度为2019*2019,问题得解,代码如下:
#include <bits/stdc++.h> using namespace std; long long L,R; int main() { scanf("%lld%lld",&L,&R); if(L%2019==0||R%2019==0) //只要有一个端点包含2019,那么乘积%2019的最小结果肯定为0 { cout<<0<<endl; return 0; } int u=L/2019;//左端点之前包含2019的个数 int v=R/2019; //右端点之前包含2019的个数 if(u<v)//说明左右端点之间肯定包含一个2019的倍数 { cout<<0<<endl; return 0; } long long ans=2020; for(long long i=L;i<=R;i++)//暴力枚举,最多2019*2019次循环 { for(long long j=i+1;j<=R;j++) ans=min((i%2019*j%2019)%2019,ans);//同余原理 } cout<<ans<<endl; return 0; }