bzoj4454 C Language Practice (O(1)gcd)

3 篇文章 0 订阅

第一行输入一个正整数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)。

利用暴力预处理出^{\sqrt{max}}之间的gcd,复杂度为l\sqrt{max}*\sqrt{max}=max

然后利用欧拉筛的原理把任意数拆成3个不大于^{\sqrt{max}}的因子,然后每次求gcd(x,y)只要枚举x的三个因子和y的关系就好了。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 1010;
namespace Input{
    const int BUF = 65536;
    char buf[BUF + 1];
    char *head = buf, *tail = buf;
}
inline char inputchar(){
    using namespace Input;
    if(head == tail)
        *(tail = (head = buf) + fread(buf, 1, BUF, stdin)) = 0;
    return *head++;
}
inline void io(int &ret){
    char ch = inputchar();
    while(ch < '0' || ch > '9')ch = inputchar();
    ret = ch - '0';ch = inputchar();
    while(ch >= '0' && ch <= '9'){
        ret = ret * 10 + ch - '0';
        ch = inputchar();
    }
}

int t,n,m,cnt,res[maxn][maxn],x[2*maxn],y[2*maxn],pri[maxn*maxn],num[maxn*maxn][3];
ll ans;
inline void up(int k,int val){
    int P=1000;
	if (num[k][0]*val<=P)num[k][0]*=val;
	else if (num[k][1]*val<=P)num[k][1]*=val;
	else num[k][2]*=val;
}
void init(){
    int P=1000;
    for(int i=0;i<=P;++i)
        res[i][0]=res[0][i]=res[i][i]=i;
    for(int i=1;i<=P;++i)
        for(int j=1;j<i;++j)
            res[i][j]=res[j][i]=res[j][i%j];
    P*=P;
    num[1][0]=num[1][1]=num[1][2]=1;
    for(int i=2;i<=P;++i){
        //cout<<i<<endl;
        if(!num[i][0]){
            pri[cnt++]=i;
            num[i][0]=i;
            num[i][1]=num[i][2]=1;
        }
        for(int j=0;j<cnt&&i*pri[j]<=P;++j){
            memcpy(num[i*pri[j]],num[i],sizeof num[i]);
            up(i*pri[j],pri[j]);
            if(i%pri[j]==0)break;
        }
    }
}
inline int gcd(int u,int v){
    int P=1000,ans=1,fa,tmp;
    if(!u||!v)return u|v;
    if(u<=P&&v<=P)return res[u][v];
    for(int i=0;i<3;++i){
        if(num[u][i]>1){
            fa=num[u][i];
            if(fa<=P)tmp=res[fa][v%fa];
            else tmp=(v%fa)?1:fa;
            ans*=tmp,v/=tmp;
        }
    }
    return ans;
}
int main(){
    init();
    for(io(t);t--;){
        unsigned ans=0;io(n),io(m);
        for(int i=0;i<n;++i)io(x[i]);
        for(int i=0;i<m;++i)io(y[i]);
        for(int i=0;i<n;++i)
            for(int j=0;j<m;++j)
                ans+=gcd(x[i],y[j])^i^j;
        printf("%u\n",ans);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值