bzoj4454: C Language Practice

本文介绍了一种高效的O(n)-O(1) GCD算法,通过预处理1到n的数的质因数分解及0到sqrt(n)之间的两两最大公约数,使得查询操作能在常数时间内完成。文中提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此题卡常卡空间。。

有一种O(n)-O(1)的Gcd:对于一个数n,它可以分解成a*b*c,(a,b,c为质数或小于sqrt(n)),证明略。

然后预处理1~n每个数的分解方式和0~sqrt(n)两两的Gcd,询问即可O(1)。

真是慢的飞起。。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1005
#define S 1000
using namespace std;
int n,m,cnt,Ans,x[2005],y[2005],T;
int a[N][N],de[N*N][3],p[N*N],t,t1;
int read()
{
	int x=0;char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
void up(int a[3],int c)
{
	if (a[0]*c<=S) a[0]*=c;
	else if (a[1]*c<=S) a[1]*=c;
	else a[2]*=c;
}
void pre(int n)
{
	for (int i=0;i<=n;i++)
		a[i][0]=a[0][i]=a[i][i]=i;
	for (int i=1;i<=n;i++)
	for (int j=1;j<i;j++)
		a[i][j]=a[j][i]=a[j][i%j];
	int m=n*n;
	de[1][0]=de[1][1]=de[1][2]=1;
	for (int i=2;i<=m;i++)
	{
		if (!de[i][0])
		{
			p[++cnt]=i;
			de[i][0]=i,de[i][1]=de[i][2]=1;
		}
		for (int j=1;j<=cnt&&i*p[j]<=m;j++)
		{
			memcpy(de[i*p[j]],de[i],sizeof de[i]);
			up(de[i*p[j]],p[j]);
			if (i%p[j]==0) break;
		}
	}
}
int Gcd(int x,int y)
{
	if (!x||!y) return x|y;
	if (x<=S&&y<=S) return a[x][y];
	int d=1;
	for (int i=0;i<3;i++)
	if (de[x][i]>1)
	{
		t1=de[x][i];
		if (t1<=S) t=a[t1][y%t1];
		else t=(y%t1)?1:t1;
		d*=t,y/=t;
	}
	return d;
}
int main()
{
	pre(S);T=read();
	while (T--)
	{
		Ans=0;n=read();m=read();
		for (int i=0;i<n;i++) x[i]=read();
		for (int i=0;i<m;i++) y[i]=read();
		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);
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值