欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【牛客练习44:C】小y的质数(求区间内k生互斥数对数---容斥原理+质因子分解)

程序员文章站 2022-06-07 16:53:24
...

题目地址:https://ac.nowcoder.com/acm/contest/634/C

题意


求区间[l,r]内有多少对k生互斥数对数,所谓k生互斥数即(y-k,y+k)互斥,且两个数都在区间内

注意:l,r可以从0开始!数据范围比较大,要转换思维,普通方法会超时

 

解题思路


(终于把大佬的代码看懂了!!(;´༎ຶД༎ຶ`) 数论是知识盲点,想不出来,想出来也不会写QAQ)

[l,r]内(x,x+2k)互斥即gcd(x,x+2k)=1也就是gcd(x,2k)=1;x对应的范围是[l,r-2*k]

举例说明:如果2k的质因子有2、3、5那么只要x含有质因子2或3或5,那么gad(x,2k)!=1

所以就要找在[l,r-2*k]内有多少数字不含有和2k相同的质因子,也就是all-被(2或3或5)整除的数字个数

两种方法:cnt为2k质因子的个数,v[]存质因子

以2k的质因子v[0]=2,v[1]=3,v[2]=5为例:

1.for循环枚举

for(ll i=0;i<1<<cnt;i++)
{
    ll p=1,f=1;
    for(ll j=0;j<cnt;j++)
    {
        if(i>>j&1)//>>的优先级比&高,判断条件:i/(2^j)是奇数
            p*=v[j],f*=-1;
    }
    ans+=x/p*f;
}

【牛客练习44:C】小y的质数(求区间内k生互斥数对数---容斥原理+质因子分解)

对于三个集合的容斥公式,应该是:【牛客练习44:C】小y的质数(求区间内k生互斥数对数---容斥原理+质因子分解)

但是在上述代码执行的过程中只多减了一个x/30,所以最后只需要再加回一个。因为是一步一步执行下来的,所以和原本的容斥原理的式子不太一样,手动模拟一下,就明白了!!

2.递归遍历枚举

work(0,1,l,r);//r=r-2*k
void work(ll i,ll x,ll l,ll r)
{
    if(i==m)
    {
        ans+=x*(r-l);
        return;
    }
    work(i+1,x,l,r);
    work(i+1,-x,l/p[i],r/p[i]);
}

把递归的树画出来最底层就是结果,和上表中最右侧的备注里的结果是一样的

14个质因子的乘积就已经超过2e+13了,所以时间复杂度在O(1e5)左右吧(没有考虑质因子分解的复杂度。。)

 

ac代码


都是参考排名前几的大佬的代码,具体是谁的写的代码忘了。。(._.)

1.for循环枚举

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <sstream>
#define  maxn 100005
typedef long long ll;
using namespace std;
ll v[maxn],l,r,k,cnt=0;
ll solve(ll x)
{
    if(x==-1) return 0;//l=0时
    ll ans=0;
    for(ll i=0;i<1<<cnt;i++)
    {
        ll p=1,f=1;
        for(ll j=0;j<cnt;j++)
        {
            if(i>>j&1)//>>的优先级比&高
                p*=v[j],f*=-1;
        }
        ans+=x/p*f;
    }
    return ans;
}
int main()
{
    scanf("%lld %lld %lld",&l,&r,&k);
    if(l+2*k>r)
        printf("0");
    else
    {
        k*=2;
        r-=k;
        for(ll i=2;i*i<=k;i++)
        {
            if(!(k%i))
            {
                v[cnt++]=i;
                while(!(k%i))
                    k/=i;
            }
        }
        if(k!=1) v[cnt++]=k;
        printf("%lld\n",solve(r)-solve(l-1));//注意写法
    }
    return 0;
}

2.递归枚举

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll l,r,n,p[100],m,i,ans;
void work(ll i,ll x,ll l,ll r)
{
    if(i==m)
    {
        ans+=x*(r-l);
        return;
    }
    work(i+1,x,l,r);
    work(i+1,-x,l/p[i],r/p[i]);
}
int main()
{
    cin>>l>>r>>n;
    n<<=1;
    r-=n;
    l--;
    if(l<0)l=0;
    if(l>=r)
    {
        puts("0");
        return 0;
    }
    for(i=2;i*i<=n;i++)if(n%i==0)
    {
        p[m++]=i;
        while(n%i==0)n/=i;
    }
    if(n>1)p[m++]=n;
    work(0,1,l,r);
    cout<<ans<<endl;
    return 0;
}