- 感慨:发现欧拉函数真的在数论中非常容易用到,它真的不仅仅是它定义的那样简单,它有很多妙用!
- 做法:题意向我们保证a = c =1;
- 所谓问题就变成了 有多少对(x,y)使得gcd(x,y) = k 并且x ∈[1, b], y ∈ [1, d]
- 利用欧拉函数思想,可以把gcd(x,y) = k 转化成 gcd(x/k,y/k) = 1 即找互质对数
- 我们可以把b设为小的那个,保障答案的唯一性,即gcd(3,1) = gcd(1,3)
- 现在分情况讨论:
- ①:当y∈[1,b/k]时,即y/k我们发现答案就是phi[1]+phi[2]+……+phi[b/k],因为欧拉函数的性质
- ②:当y∈[b/k,d/k]时,就不能使用欧拉函数了。
- 这时候就要考虑如何求
- y∈[b/k,d/k]中与[1,b/k] 互质对数有多少对,我们可以先求这个答案的逆,即y的质数因子在1~b/k范围内能整除的个数
- PS:这里需要预处理好y的质因数
- 咦~我们发现酱紫不就可以使用容斥原理了嘛。
- y的质数因子在1~b/k范围内能整除的个数 = 每个质因子的倍数的个数-两个质因子乘积的倍数的个数+三个……
- 然后互质个数就是b/k 减去 y的质数因子在1~b/k范围内能整除的个数
- 看了一波kuangbin大大的模拟队列实现容斥感觉不错,接过来学习了~
AC代码:KuangBin版的容斥
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const double PI = 4*atan(1.0);
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
int phi[maxn],prime[maxn];
int que[maxn];
int tot;
vector<int> v[maxn];
void phi_table()//线性筛法欧拉函数表
{
phi[1] = 1;
for(int i=2;i<maxn;i++){
if(!phi[i]){
phi[i] = i-1;
prime[tot++] = i;
}
for(int j=0;j<tot && 1LL*i*prime[j]<maxn;j++){
if(i%prime[j]) phi[i*prime[j]] = phi[i]*(prime[j]-1);
else{
phi[i*prime[j]] = phi[i]*prime[j];
break;
}
}
}
}
void dec(int N)//质因数分解
{
for(int i=2;i<=N;i++) v[i].clear();
for(int i=2;i<=N;i++){
int t = i;
for(int j=2;j*j<=t;j++){
if(t%j == 0){
v[i].push_back(j);
while(t%j == 0) t/=j;
}
}
if(t>1) v[i].push_back(t);
}
}
ll cal(int n,int x) //数组模拟队列
{
int num = 0;
que[num++] = -1;
for(int i=0;i<(int)v[x].size();i++)
{
int e = v[x][i];
if(e>n) break;
int k = num;
for(int j=0;j<k;j++)
{
que[num] = que[j]*e*(-1);
num++;
}
}
ll sum = 0;
for(int i=1;i<num;i++)
{
sum = sum+(n/que[i]);
}
return 1LL*n-sum;
}
int main()
{
#ifdef LOCAL_FILE
freopen("in.txt","r",stdin);
#endif // LOCAL_FILE
ios_base::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t,id = 1;
phi_table();
dec(maxn-2);
cin>>t;
while(t--)
{
int a,b,c,d,k;
cin>>a>>b>>c>>d>>k;
if(k == 0 || k>b || k>d)
{
cout<<"Case "<<id++<<": "<<0<<endl;
continue;
}
if(b>d) swap(b,d);
ll ans = 0;
b/=k;
d/=k;
for(int i=1;i<=b;i++) //求出[1,b/k]内的对数
ans+=phi[i];
for(int i=b+1;i<=d;i++)
{
ans += cal(b,i);
}
cout<<"Case "<<id++<<": "<<ans<<endl;
}
return 0;
}
AC代码:枚举子集的容斥(跟大佬们学习的~)
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const double PI = 4*atan(1.0);
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
int phi[maxn],prime[maxn];
int que[maxn];
int tot;
vector<int> v[maxn];
void phi_table()//线性筛法欧拉函数表
{
phi[1] = 1;
for(int i=2;i<maxn;i++){
if(!phi[i]){
phi[i] = i-1;
prime[tot++] = i;
}
for(int j=0;j<tot && 1LL*i*prime[j]<maxn;j++){
if(i%prime[j]) phi[i*prime[j]] = phi[i]*(prime[j]-1);
else{
phi[i*prime[j]] = phi[i]*prime[j];
break;
}
}
}
}
void dec(int N)//质因数分解
{
for(int i=2;i<=N;i++) v[i].clear();
for(int i=2;i<=N;i++){
int t = i;
for(int j=2;j*j<=t;j++){
if(t%j == 0){
v[i].push_back(j);
while(t%j == 0) t/=j;
}
}
if(t>1) v[i].push_back(t);
}
}
ll cal(int n,int x) //枚举子集的容斥原理
{
int tmp,flag;
int sum = 0;
for(int i=1;i<(1<<v[x].size());i++)
{
tmp = 1; flag = 0;
for(int j=0;j<(int)v[x].size();j++)
{
if(i&(1<<j)){ //判断当前情况有多少个质因子
flag++;
tmp = tmp*v[x][j];
}
}
if(flag&1) sum += n/tmp; //奇加 (如果有奇数个质因子)
else sum-= n/tmp; //偶减 (如果有偶数个质因子)
}
return 1LL*n-sum;//逆逆得正( ̄▽ ̄)/
}
int main()
{
#ifdef LOCAL_FILE
freopen("in.txt","r",stdin);
#endif // LOCAL_FILE
ios_base::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t,id = 1;
phi_table();
dec(maxn-2);
cin>>t;
while(t--)
{
int a,b,c,d,k;
cin>>a>>b>>c>>d>>k;
if(k == 0 || k>b || k>d)
{
cout<<"Case "<<id++<<": "<<0<<endl;
continue;
}
if(b>d) swap(b,d);
ll ans = 0;
b/=k;
d/=k;
for(int i=1;i<=b;i++) //求出[1,b/k]内的对数
ans+=phi[i];
for(int i=b+1;i<=d;i++)
{
ans += cal(b,i);
}
cout<<"Case "<<id++<<": "<<ans<<endl;
}
return 0;
}