题意
给定数组(a(left| a ight|leq 10^5))和整数(k(2leq k leq 100)),问满足一下条件的二元组(<i,j>)的数目:
- (1 leq i <jleq n)
- (exist x,a_i cdot a_j=x^k)
解题思路
其实就是求
[sum_{i=1}^{n-1}sum_{j=i+1}^n left [ a_i cdot a_j=x^k
ight]
]
把(x)提出来,式子变为
[sum_{i=1}^nsum_{x=1}^{x^kleq10^{10}} cnt_{x^k/a_i},其中cnt_j表示当前j出现的次数
]
这样求的复杂度是(O(n10^{frac{10}{k}})),在(k geq 3)的时候是足够优秀的,所以需要特判一下(k=2)的情况。
如果将整数看成多个素数的乘积,即(n=Pi_i p_i^{x_i})
那么两个整数相乘的结果是平方数(Leftrightarrow)对应素数的幂次应该同奇偶
所以用一个bitset表示,用map记录一下,这个问题就解决了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int cntp=1e4+5; //1e5内素数的个数,一开始bitset开1e5的大小MLE了
unordered_map<bitset<cntp>,int>mp;
int n,m,k,a[maxn],cnt[maxn],p[maxn],tot;
ll ans,x[maxn];
inline ll qp(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a;
a=a*a;
b>>=1;
}
return res;
}
inline bool check(int x){
for(int i=2;i<=sqrt(x);i++){
if(x%i==0)return false;
}
return true;
}
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
if(k==2){
for(int i=2;i<maxn;i++)if(check(i))p[++tot]=i;
bitset<cntp>b;
for(int i=1;i<=n;i++){
b.reset(); b.set(0);
ll tmp=a[i];
int cnt=0;
for(int j=1;j<=tot;j++){
while(tmp%p[j]==0){++cnt; tmp/=p[j];}
if(cnt&1)b.set(j);
cnt=0;
if(tmp==1)break;
}
ans+=mp[b];
mp[b]++;
}
}
else{
for(ll i=1;;i++){
x[i]=qp(i,k);
if(x[i]>=1e10){
m=i;
break;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]>x[j])continue;
if(x[j]%a[i]==0 && x[j]/a[i]<maxn){
ans+=cnt[x[j]/a[i]];
}
}
++cnt[a[i]];
}
}
printf("%lld
",ans);
return 0;
}