版权声明:编写不易,转载请注明出处,谢谢。 https://blog.csdn.net/WilliamSun0122/article/details/77161256
题意
题目还是比较好懂的,就是给你一个区间[l,r]和一个数n,问你这个区间中有多少个数和n互质。
题解
容斥的思想还是很好懂的,就是把求集合的并转换成求集合的交。但是用代码表示出来还是有难度的。
这一题给的区间范围还是很大的,我们直接求有多少个数与n互质不是很好求,可以先用补集的思想求有多少个数与n不互质。一个数与n不互质等价于其和n有一个不为1的公因子。此时问题就是求区间[l,r]中有多少个数含有因子x,这个问题也不是很好求,但是求区间[1,m]有多少个数含有因子x就好求了,就等于m/x。所以我们可以先求区间[1,r],再求区间[1,l],最后一减就可以了。
最后还有一个问题就是n的因子会有很多(比如有x,y,z三个因子),区间中有的数可能可能既含有因子x也含有因子y,这样就会重复计数,所以我们要用容斥原理筛去。
具体如何容斥看代码注释。
这里容斥有两种写法,加奇减偶和加偶减奇。(其实本质都是一样的)
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
ll a,b,totle;
int n,m;
vector<int> f;
ll solve(ll x) //做容斥
{
ll ans = 0; //ans是区间[1,x]中与n不互质的数的个数
for(ll i=1;i<=totle;i++) //遍历1到所有状态
{
//cnt是含有因子的个数,se是含有的因子最后的乘积
int se=1,cnt=0;
for(int j=0;j<m;j++)
{
if((i>>j)&1) se *= f[j],cnt++;
}
if(cnt&1) ans += x/se; //加奇
else ans -= x/se; //减偶
}
//加奇减偶最后求出来的ans就是区间中与n不互质的数的个数
//再用区间总数减去ans就是区间中与n互质的数的个数
return x-ans;
}
int main()
{
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
scanf("%lld%lld%d",&a,&b,&n);
f.clear();
for(int i=2;i*i<=n;i++)//筛出n的因子
{
if(n%i==0)
{
f.push_back(i);
while(n%i==0) n /= i;
}
}
if(n!=1) f.push_back(n);
m = f.size();
//用m个二进制位表示n因子的状态,比如n有两个因子2,3
//那么0就是00代表两个因子都不含有
//1就是01代表含有3不含2
//2就是10代表含有2不含3
//3就是11代表2,3都含有
totle = (1<<m)-1;
printf("Case #%d: %lld\n",ca,solve(b)-solve(a-1));
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
ll a,b,totle;
int n,m;
vector<int> f;
ll solve(ll x)
{
ll ans = 0; //ans求出的就是区间中与n互质的数的个数
//这里与上一个不同的是从0开始遍历所有状态
for(ll i=0;i<=totle;i++)
{
int se=1,cnt=0;
for(int j=0;j<m;j++)
{
if((i>>j)&1) se *= f[j],cnt++;
}
if(cnt&1) ans -= x/se; //减奇
else ans += x/se; //加偶
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
scanf("%lld%lld%d",&a,&b,&n);
f.clear();
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
f.push_back(i);
while(n%i==0) n /= i;
}
}
if(n!=1) f.push_back(n);
m = f.size();
totle = (1<<m)-1;
printf("Case #%d: %lld\n",ca,solve(b)-solve(a-1));
}
return 0;
}
上面两种代码,可以说本质上是一样的。就是减奇加偶的从0开始遍历所有状态,其中的0状态就代表了加奇减偶中的区间总数。