AtCoder Beginner Contest 206 E - Divide Both(容斥)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 206 E - Divide Both(容斥)相关的知识,希望对你有一定的参考价值。
不是多么难的一题,但是却写了很久,下次不能再脑溢血了…
题意
求 x ∈ [ L , R ] & & y ∈ [ L , R ] x\\in[L,R]\\&\\&y\\in[L,R] x∈[L,R]&&y∈[L,R]的合法 ( x , y ) (x,y) (x,y)对的数量
其中设 g c d ( x , y ) = k gcd(x,y)=k gcd(x,y)=k,满足 k ! = 1 & & k ! = x & & k ! = y k!=1\\&\\&k!=x\\&\\&k!=y k!=1&&k!=x&&k!=y
考虑到 L , R L,R L,R范围不大,枚举每个值单独进行计算
枚举 x x x,我们考虑计算 ( x , y ) & & y > x (x,y)\\&\\&y>x (x,y)&&y>x的方案有多少
让我们先不管是否满足要求,先求出 [ x + 1 , R ] [x+1,R] [x+1,R]中与 x x x不互质的数有多少
这个我们可以对 x x x分解质因子,只要包含任意一个 x x x的质因子就不互质
所以我们加上包含有一个质因子的,减去有两个质因子的,加上有三个质因子的…
但是这么算还是重复,因为自己不能作为 g c d gcd gcd
所以我们再减去 R / x − 1 R/x-1 R/x−1即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9+7;
const int maxn = 1e6+10;
int L,R,vis[maxn],prime[maxn],mi[maxn],top;
vector<int>vec;
void init()
{
for(int i=2;i<=1000000;i++)
{
if( !vis[i] ) prime[++top] = i, mi[i] = i;
for(int j=1;j<=top&&prime[j]*i<=1000000;j++)
{
vis[prime[j]*i] = 1; mi[prime[j]*i] = prime[j];
if( i%prime[j]==0 ) break;
}
}
}
int get(int x,int L,int R)
{
while( x!=1 )
{
vec.push_back( mi[x] );
int k = mi[x];
while( x!=1 && mi[x]==k ) x /= mi[x];
}
int w = vec.size(), ans = 0;//先找和自己不互质的数
for(int i=1;i<(1<<w);i++)
{
int r = 1, bit = 0;
for(int j=0;j<w;j++)
if( (i>>j)&1 ) r *= vec[j], bit++;
if( bit&1 ) ans += R/r-(L-1)/r;
else ans -= R/r-(L-1)/r;
}
vec.clear();
return ans;
}
signed main()
{
init();
cin >> L >> R;
L = max( L,2ll ), R = max( R,L );
long long ans = 0;
for(int i=L;i<=R;i++) ans += get(i,i+1,R);
for(int i=L;i<=R;i++) ans -= (R/i-1);
cout << ans*2;
}
也有另外一种做法
我们直接枚举 x x x作为 g c d gcd gcd,其中 x x x不应该含有两个相同的质因子,否则会算重复
显然有 k = R / x − ( L − 1 ) / x k=R/x-(L-1)/x k=R/x−(L−1)/x个数包含因子 x x x
那么答案是加上 k ∗ ( k − 1 ) / 2 k*(k-1)/2 k∗(k−1)/2吗?
然而,这 k ∗ ( k − 1 ) / 2 k*(k-1)/2 k∗(k−1)/2的数对确实有公因子 x x x,但可能他们的 g c d gcd gcd是 f x fx fx,其中 f f f未知
所以我们采用容斥的做法,若 x x x有奇数个质因子,加上答案,否则减去答案.
其实和上面的道理差不多。
贴一个官方题解的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
long long l,r;
cin >> l >> r;
long long res=0;
vector<int> cnt(1048576,0);
for(long long i=2;i<=r;i++){
if(cnt[i]!=0){continue;}
for(long long j=i;j<=r;j+=i){cnt[j]++;}
for(long long j=i*i;j<=r;j+=i*i){cnt[j]=-1000000007;}
}
for(long long i=2;i<=r;i++){
if(cnt[i]<0){continue;}
long long cc=(r/i)-((l-1)/i);
if(cnt[i]%2){res+=(cc*(cc-1))/2;}
else{res-=(cc*(cc-1))/2;}
}
for(long long i=max(2ll,l);i<=r;i++){res-=(r/i-1);}
cout << 2*res << '\\n';
return 0;
}
以上是关于AtCoder Beginner Contest 206 E - Divide Both(容斥)的主要内容,如果未能解决你的问题,请参考以下文章
AtCoder Beginner Contest 115 题解