uoj 48 核聚变反应强度 次小公因数
【UR #3】核聚变反应强度
Time Limit: 1 Sec Memory Limit: 256 MB
题目连接
http://uoj.ac/problem/48Description
核聚变特征值分别为 x 和 y 的两个原子进行核聚变,能产生数值为 sgcd(x,y) 的核聚变反应强度。
其中, sgcd(x,y) 表示 x 和 y 的次大公约数,即能同时整除 x,y 的正整数中第二大的数。如果次大公约数不存在则说明无法核聚变, 此时 sgcd(x,y)=−1。
现在有 n 个原子,核聚变特征值分别为 a1,a2,…,an。然后 Picks 又从兜里掏出一个核聚变特征值为 a1 的原子,你需要计算出这个原子与其它 n 个原子分别进行核聚变反应时的核聚变反应强度,即 sgcd(a1,a1),sgcd(a1,a2),…,sgcd(a1,an)。
Input
第一行一个正整数 n。第二行 n 个用空格隔开的正整数,第 i 个为 ai。
Output
C/C++ 输入输出 long long 时请用 %lld。由于本题数据量较大,建议不要使用 cin/cout 进行输入输出。
Sample Input
12450 1 2 450
Sample Output
HINT
n≤105,ai≤1012
题意
题解:
算法一
对于 n=1 的数据,就是求一个数次大的约数。
众所周知一个数x的约数是成对出现的(d、xd),其中总有一个不超过x√。所以从1到a1−−√地枚举d就能找出所有a1的约数了。排序输出次大的即可。
复杂度:O(a√)
算法二
先找出a1的所有约数,然后枚举i,sgcd(a1,ai)显然也是a1的约数,所以枚举a1的所有约数,找到是ai约数的次大的即可。
复杂度:O(na√)
算法三
考虑分解质因子后:
a=px11px22...pxmm
b=py11py22...pymm
则:gcd(a,b)=pmin(x1,y1)1pmin(x2,y2)2...pmin(xm,ym)m
我们发现,a和b的公约数都一定是gcd(a,b)的约数。那么为了得到次大公约数,只需求出gcd(a,b),再除去一个最小的公共质因子即可。
对a1用O(a1−−√)的时间分解得到O(log(a1))个质因数,每次对于ai,先求出g=gcd(a1,ai),然后枚举a1的每个质因数,找到最小的能整除g那个,设其为p,g/p即为所求。(不存在则为输出−1)
复杂度:O(a√+nlog(a))
一个骗分算法
考虑算法二,我们预先对 a1 的约数们排好序,然后枚举 i,从约数表里每次二分到 gcd(a1,ai)所在位置,再往前枚举,找到第一个能整除ai的即为次大公约数。
虽然复杂度不靠谱,但是对于ai≤1012的范围实际运行速度十分优秀。需要构造针对的数据才能卡住。
还有另一个骗分算法,求出 gcd 然后暴力枚举最小质因子。好多人写这个啊……你们都没意识到复杂度不对么……放你们一马给了 80 分。
(有这种闲心的为啥不写正解啊,你们考虑过 maker 的感受吗!QAQ)
代码:
//qscqesze #include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <sstream> #include <queue> #include <typeinfo> #include <fstream> #include <map> #include <stack> typedef long long ll; using namespace std; //freopen("D.in","r",stdin); //freopen("D.out","w",stdout); #define sspeed ios_base::sync_with_stdio(0);cin.tie(0) #define maxn 200001 #define mod 10007 #define eps 1e-9 int Res,Num;char C,CH[12]; //const int inf=0x7fffffff; //无限大 const int inf=0x3f3f3f3f; /* inline void P(int x) { Num=0;if(!x){putchar('0');puts("");return;} while(x>0)CH[++Num]=x%10,x/=10; while(Num)putchar(CH[Num--]+48); puts(""); } */ //************************************************************************************** inline ll read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void P(int x) { Num=0;if(!x){putchar('0');puts("");return;} while(x>0)CH[++Num]=x%10,x/=10; while(Num)putchar(CH[Num--]+48); puts(""); } ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b); } ll a[maxn]; ll p[maxn]; int main() { int n=read(); for(int i=0;i<n;i++) scanf("%lld",&a[i]); ll tot=0; ll x=a[0]; for(ll i=2;i<=sqrt(x);i++) { if(x%i==0) { while(x%i==0)x/=i; p[tot++]=i; } } for(int i=0;i<n;i++) { ll d=gcd(a[0],a[i]); int flag=1; for(int j=0;j<tot;j++) { if(d%p[j]==0) { printf("%lld ",d/p[j]); flag=0; break; } } if(flag) { if(d!=1) printf("1 "); else printf("-1 "); } } }