嗯……难度在于$O(N)-O(1)$求GCD
首先每个数都小于等于1000000
然后我们对于$1000$以内的数预处理
然后设两个数为$x$,$y$,要求的为$gcd(x,y)$
首先每个数显然最多分成3个数,每个都小于1000或者是一个质数
然后我们考虑若$d|gcd(x,y)$,那么答案可以改写成$d*gcd(x/d,y/d)$
然后对这三个数中是质数的这么复合一下,不是质数的利用$gcd(x,y)=gcd(y,x\%y)$查表即可。(因为此时拆开的$x<=1000$,所以此时$x\%y<=1000$)
最后复合其实就是乘一下……
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000;
int test,n,m,gcd[N+5][N+5],a[N*2+5],b[N*2+5],pri[N*79],pcnt,f[N*N+5],vs[N*N+5][3];
unsigned int ans;
template<typename T>inline void Read(T &x){
static char ch;
while((ch=getchar())<'0' || ch>'9');
x=ch-'0';
while((ch=getchar())>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0';
}
inline int GCD(int x,int y){
if(!x || !y)return x+y;
if(x<=N && y<=N)return gcd[x][y];
int g=1;
for(register int i=0;i<3;i++){
if(vs[x][i]>1){
int w=vs[x][i];
if(f[w]==w){if(y%w==0){g*=w;y/=w;}}
else {g*=gcd[w][y%w];y/=gcd[w][y%w];}
}
}
return g;
}
inline void Prepare(){
for(register int i=1;i<=N;i++)gcd[0][i]=gcd[i][0]=i;
for(register int i=1;i<=N;i++)for(register int j=1;j<=i;j++)gcd[i][j]=gcd[j][i]=gcd[j][i%j];
f[1]=1;
for(register int i=2;i<=N*N;i++){
if(!f[i])f[i]=i,pri[++pcnt]=i;
for(register int j=1;j<=pcnt && pri[j]*i<=N*N;j++){
f[i*pri[j]]=pri[j];
if(i%pri[j]==0)break;
}
}
vs[1][0]=vs[1][1]=vs[1][2]=1;
for(register int i=2;i<=N*N;i++){
vs[i][0]=vs[i/f[i]][0];
vs[i][1]=vs[i/f[i]][1];
vs[i][2]=vs[i/f[i]][2];
if(vs[i][0]*f[i]<=N)vs[i][0]*=f[i];
else if(vs[i][1]*f[i]<=N)vs[i][1]*=f[i];
else vs[i][2]*=f[i];
}
}
int main(){
freopen("4454.in","r",stdin);
freopen("4454.out","w",stdout);
Read(test);
Prepare();
while(test--){
Read(n);Read(m);ans=0;
for(register int i=0;i<n;i++)Read(a[i]);
for(register int i=0;i<m;i++)Read(b[i]);
for(register int i=0;i<n;i++)for(register int j=0;j<m;j++)ans+=GCD(a[i],b[j])^i^j;
printf("%u\n",ans);
}
return 0;
}
评论 (0)