Codeforces 1016G Appropriate Team 数学

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33229466/article/details/82284736

题意

给出一个长度为n的数组a和两个数X,Y,问有多少个数对(i,j)满足存在一个数v使得gcd(a[i],v)=X且lcm(a[j],v)=Y。
n 200000 , X , Y , a [ i ] 10 18

分析

显然X一定是Y的约数,然后有X是a[i]的约数,a[j]是Y的约数。
然后考虑X和Y所有指数不一样的质因子,对于某个数a[i],如果对于某个质因子p,a[i]的指数大于X的指数,则p在v中的指数一定等于X的指数,否则就可以去任意不小于X指数的整数。
对a[j]和Y也做同样的处理,这时我们可以把每个数的限制用一个二进制表示,发现如果(i,j)合法,那么a[i]对应的二进制 and a[j]对应的二进制=0。
用一个前缀和处理一下即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

typedef long long LL;

const int N=200005;

int n,bin[20],f[1000005],po[20],ppo[N][20];
LL a[N],X,Y,tot,pri[20];

LL gcd(LL x,LL y)
{
    if (!y) return x;
    else return gcd(y,x%y);
}

void add(LL p)
{
    int s1=0,s2=0;LL t1=X,t2=Y;
    while (t1%p==0) t1/=p,s1++;
    while (t2%p==0) t2/=p,s2++;
    if (s1<s2) pri[++tot]=p;
}

void divi(LL r)
{
    for (LL i=2;i<=1000000;i++)
        if (r%i==0)
        {
            add(i);
            while (r%i==0) r/=i;
        }
    if (r==1) return;
    LL w=sqrt(r);
    if (w*w==r) add(w);
    else
    {
        a[0]=X;
        for (int i=0;i<=n;i++)
        {
            w=gcd(a[i],r);
            if (w>1&&w<r) {add(w);add(r/w);return;}
        }
        add(r);
    }
}

int main()
{
    scanf("%d%lld%lld",&n,&X,&Y);
    if (Y%X!=0) {puts("0");return 0;}
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    divi(Y);
    bin[0]=1;
    for (int i=1;i<=tot;i++) bin[i]=bin[i-1]*2;
    for (int i=1;i<=tot;i++)
    {
        LL tmp=X;po[i]=0;
        while (tmp%pri[i]==0) tmp/=pri[i],po[i]++;
    }
    for (int i=1;i<=n;i++)
    {
        int mask=0;LL tmp=a[i];
        for (int j=1;j<=tot;j++)
        {
            while (tmp%pri[j]==0) tmp/=pri[j],ppo[i][j]++;
            if (ppo[i][j]>po[j]) mask+=bin[j-1];
        }
        if (a[i]%X==0) f[mask]++;
    }
    for (int j=0;j<tot;j++)
        for (int i=0;i<bin[tot];i++)
            if (i&bin[j]) f[i]+=f[i-bin[j]];
    LL ans=0;
    for (int i=1;i<=tot;i++)
    {
        LL tmp=Y;po[i]=0;
        while (tmp%pri[i]==0) tmp/=pri[i],po[i]++;
    }
    for (int i=1;i<=n;i++)
    {
        if (Y%a[i]!=0) continue;
        int mask=0;
        for (int j=1;j<=tot;j++) if (ppo[i][j]<po[j]) mask+=bin[j-1];
        ans+=f[mask^(bin[tot]-1)];
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/82284736