BZOJ4454: C Language Practice
Description
Input
第一行输入一个正整数T(T<=85),表示测试数据的组数。
每组数据第一行包含两个正整数n,m(1<=n,m<=2000),表示序列的长度。
第二行包含n个正整数,表示a[0],a[1],...,a[n-1](0<=a[i]<=1000000)。
第三行包含m个正整数,表示b[0],b[1],...,b[m-1](0<=b[i]<=1000000)。
Output
对于每组数据输出一行一个整数,即答案。
Sample Input
3
3 2
5 9 6
3 4
2 2
8 9
0 6
1 1
9
6
3 2
5 9 6
3 4
2 2
8 9
0 6
1 1
9
6
Sample Output
6
22
3
22
3
传说中的O(n)-O(1)求gcd算法。
这个算法基于这样一个定理:
对于一个正整数x,x一定可以分解成x1*x2*x3的形式。其中xi满足要么小于等于sqrt(x),要么是一个素数。
然后整个算法分为两步:
1.预处理出1到N所有数的分解式与1到sqrt(N)内两两数的gcd。
2.对于每个x,y,O(1)回答gcd(x,y)。
预处理过程
预处理1到sqrt(N)内两两数的gcd可以递推。
预处理1到N所有数的分解式的话可以线性筛弄出每个数x最小的质因子p,再根据p的分解式得到x的分解式。
询问过程
因为xi要么<=sqrt(N)要么是一个素数,根据这个性质就可以写出代码:
int SIZE=(int)sqrt(N); int ans=1,d; for(int i=0;i<3;i++) { if(x[i]<=SIZE) d=gcd[x[i]][y%x[i]]; else if(y%x[i]==0) d=x[i]; else d=1; ans=ans*d;y=y/d; }
正确性显然。
全代码:
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=1000010; int g[1010][1010],d[maxn],pri[maxn/10],cnt; bool vis[maxn]; int A[maxn],B[maxn],C[maxn]; void init(int n) { rep(i,0,1005) rep(j,0,i) { if(!i||!j) g[i][j]=i+j; else g[i][j]=g[j][i%j]; } rep(i,0,1005) rep(j,i+1,1005) g[i][j]=g[j][i]; d[1]=1; rep(i,2,n) { if(!vis[i]) pri[++cnt]=i,d[i]=i; rep(j,1,cnt) { if(i*pri[j]>n) break; vis[i*pri[j]]=1; if(i%pri[j]==0) {d[i*pri[j]]=d[i];break;} d[i*pri[j]]=pri[j]; } } A[1]=B[1]=C[1]=1; rep(i,2,n) { int j=i/d[i];A[i]=A[j];B[i]=B[j];C[i]=C[j]; if(A[i]*d[i]<=1000) A[i]*=d[i]; else if(B[i]*d[i]<=1000) B[i]*=d[i]; else C[i]*=d[i]; } } int X[3]; int gcd(int x,int y) { if(!x||!y) return x+y; if(x<=1000&&y<=1000) return g[x][y]; int c=0; if(A[x]!=1) X[c++]=A[x]; if(B[x]!=1) X[c++]=B[x]; if(C[x]!=1) X[c++]=C[x]; int ans=1,d; rep(i,0,c-1) { if(X[i]<=1000) d=g[X[i]][y%X[i]]; else if(y%X[i]==0) d=X[i]; else d=1; ans*=d;y/=d; } return ans; } int n,m,a[2010],b[2010]; int main() { init(1000000); dwn(T,read(),1) { n=read();m=read();unsigned int ans=0; rep(i,0,n-1) a[i]=read(); rep(i,0,m-1) b[i]=read(); rep(i,0,n-1) rep(j,0,m-1) ans+=gcd(a[i],b[j])^i^j; printf("%u\n",ans); } return 0; }