SP11391 EASYMATH - EASY MATH 题解
这题很好得结合了数论和组合的知识,有点 AtCoder 的风格。
首先我们来分析题目。如果使用枚举算法必定会超时;所以我们需要使用数论的方法。
我们先定义一个函数
接下来是本题的重点。我们先来看本问题的简化版:
求区间
我们先考虑能被
很显然结果不是
所以,最终答案是
有没有觉得上面的公式很熟悉?
没错,它就是大名鼎鼎的容斥原理。
根据容斥原理,我们就可以得出本题对于
直接上结论吧:
设
本题中
放代码:
#include<bits/stdc++.h>
#define int long long // 记得开 long long
using namespace std;
int lcm(int n,vector<int> a){
int s=1;
for(int i=0;i<n;i++)
s=s/gcd(a[i],s)*a[i];
return s;
} // 求 lcm 的过程,可使用 C++17 的 gcd() 函数
int f(int l,int r,int x){
int l2=l%x?l/x+1:l/x,r2=r/x;
return r2-l2+1;
} // 上文所述的 f(l,r,x) 函数
main(){
ios::sync_with_stdio(false);
int t; cin>>t;
while(t--){
int n,m,a[5],d,c=0; cin>>n>>m>>a[0]>>d;
for(int i=1;i<5;i++)a[i]=a[i-1]+d;
for(int i=0;i<5;i++)c+=f(n,m,a[i]); // 考虑能被单个整数整除的整数个数
for(int i=0;i<4;i++)
for(int j=i+1;j<5;j++)
c-=f(n,m,lcm(2,{a[i],a[j]})); // 考虑能被 2 个整数同时整除的整数个数,下同
for(int i=0;i<3;i++)
for(int j=i+1;j<4;j++)
for(int k=j+1;k<5;k++)
c+=f(n,m,lcm(3,{a[i],a[j],a[k]}));
for(int i=0;i<2;i++)
for(int j=i+1;j<3;j++)
for(int k=j+1;k<4;k++)
for(int l=k+1;l<5;l++)
c-=f(n,m,lcm(4,{a[i],a[j],a[k],a[l]}));
cout<<(m-n+1)-c-f(n,m,lcm(5,{a[0],a[1],a[2],a[3],a[4]}))<<endl; // 答案输出
} // 本题为多测
return 0;
}