【Codeforces 1016G】Appropriate Team
Pollard Rho,状态压缩,子集和。
题目大意:
给定 $a_1,a_2,a_3,\ldots,a_n$ 和 $X,Y$。
问有多少有序数对 $(i,j)$,满足:
存在一个正整数 $v$,有 $\gcd(a_i,v)=X$,$\mathrm{lcm}(a_j,v)=Y$。
题解:
我们对每个质因子分别考虑。
考虑 $\gcd(a_i,v)=X$ 的情况。对于一个质因子 $p$,如果 $a_i$ 中有 $m$ 个,$v$ 中有 $n$ 个,$X$ 中有 $k$ 个,那么 $\gcd$ 中就应当有 $\min(m,n)$ 个,即 $k=\min(m,n)$。
那么有以下几种情况:
- $m\lt k$。这种情况无法满足条件。
- $m\gt k$。这种情况只有 $n=k$ 才能满足条件。
- $m=k$。这种情况只要 $n\geq k$ 就可以满足条件。
我们称满足条件 $3$ 的质因子为自由因子。
$\mathrm{lcm}$ 也是类似的定义有自由因子。
$10^{18}$ 里,不同质因子个数最多大概也就是十六七个,所以我们可以对每个 $a_i$,用二进制数来状压 $\gcd$ 和 $\mathrm{lcm}$ 的自由因子。
考虑 $\gcd$ 的自由因子和 $\mathrm{lcm}$ 的自由因子分别为 $x,y$ 的情况。
我们仍然对于每一位进行考虑。
不难发现,每一位必须满足以下条件之一,这对数才能产生 $1$ 的贡献:
- 在 $x$ 中或者 $y$ 中,这一位是自由因子。
- 在 $X$ 中和 $Y$ 中这个质因子的出现次数相等。
那么对于 $y$,我们可以先把 $X$ 中和 $Y$ 中这个质因子的出现次数相等的那些位钦定为自由因子,然后只需要当前的 $y’$ 来考虑条件 $1$ 即可。
我们发现,$x$ 和 $y’$ 能产生贡献,当且仅当 $x$ 的补集是 $y’$ 的子集。
我们用数组存 $\gcd$ 的每种不同的自由因子状态(的补集)的出现次数,然后对其做子集和变换。
然后对于一个 $y$ 的贡献就可以快速计算了。
至于分解质因数,我们只需要对 $Y$ 进行 pollard_rho 即可得出需要考虑的那些因数。$a$ 中其他的因数可以不用考虑。至于 $X$ 中如果出现了在 $Y$ 中不存在的质因子,那么答案就是 $0$。
时间复杂度 $O(\sqrt {\sqrt Y}+3^{17}+17n)$。
Code:
#include<vector>
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
const int N=2e5+5;
typedef long long LL;
inline LL mul(LL a,LL b,const LL&md){
LL c=a*b-(LL)((long double)a*b/md+.5)*md;
return(c>>63&md)+c;
}
inline LL pow(LL a,LL b,const LL&md){
LL ret=1;for(a%=md;b;b>>=1,a=mul(a,a,md))if(b&1)ret=mul(ret,a,md);return ret;
}
namespace MR{
const int P[]={2,3,5,7,11,13,17,19,23,29,31,37};
bool check(int a,LL n){
LL x=n-1;int d=0;
while(!(x&1))x>>=1,++d;
for(x=pow(a,x,n);d--;){
const LL lst=x;x=mul(x,x,n);
if(x==1&&lst!=1&&lst!=n-1)return 0;
}
return x==1;
}
bool test(LL n){
if(n==1)return 0;
for(int i:P)if(n==i)return 1;
if(!(n&1))return 0;
for(int i:P)if(!check(i,n))return 0;
return 1;
}
}
vector<LL>v;
LL find(LL n,int c){
LL x=rand()%(n-1)+1,y=x,k=2,q=1,t=1;
for(;;k<<=1,y=x,q=1){
for(int i=1;i<=k;++i){
x=mul(x,x,n)+c-n;x+=x>>63&n;
q=mul(q,llabs(x-y),n);
if(!(i&127)){
t=std::__gcd(q,n);
if(t>1)return t;
}
t=std::__gcd(q,n);
if(t>1)return t;
}
}
}
void rho(LL n){
if(n==1)return;
if(MR::test(n))return v.push_back(n);
LL p=n;int c=19260817;
while(p==n)p=find(p,c--);
while(n%p==0)n/=p;
rho(p),rho(n);
}
int n;LL X,Y;
int fx[23],fy[23],fz[23];
LL cX[1<<18],cY[1<<18];
void get(LL x,int*f){
for(int i=0;i<v.size();++i)f[i]=0;
for(int i=0;i<v.size();++i)
while(x%v[i]==0)++f[i],x/=v[i];
}
int main(){
srand(time(0));
scanf("%d%lld%lld",&n,&X,&Y);
if(Y%X)return puts("0"),0;
rho(Y);
sort(v.begin(),v.end());
get(X,fx),get(Y,fy);
const int C=(1<<v.size())-1;
for(int i=1;i<=n;++i){
LL x;
scanf("%lld",&x);
get(x,fz);
if(x%X==0){
int zt=0;
for(int i=0;i<v.size();++i)if(fz[i]==fx[i])zt|=1<<i;
++cX[C^zt];
}
if(Y%x==0){
int zt=0;
for(int i=0;i<v.size();++i)if(fz[i]==fy[i])zt|=1<<i;
++cY[zt];
}
}
LL ans=0;
for(int i=C;i;--i)
for(int j=(i-1)&i;;j=(j-1)&i){cX[i]+=cX[j];if(!j)break;}
for(int i=0;i<=C;++i){
int zt=i;
for(int j=0;j<v.size();++j)if(fx[j]==fy[j])zt|=1<<j;
ans+=cY[i]*cX[zt];
}
printf("%lld",ans);
return 0;
}