题目链接:http://codeforces.com/contest/1025/problem/B
题意:
给出n个数对,一个数对有两个数字,输出一个值,这个值能整除每个数对中任意一个数字,并输出这个值,多个解输出一个就行,这个值如果是1,那么就输出-1;
思路:(我自己平时最怕的就是数学题,一些整除或者倍数关系什么的,可能会表述错误,但是代码是能AC的,要是文字说明看不懂的话就直接看代码吧)
先看看暴力的方法:
因为要求的这个数字能够被每一个数对中任意一个数字整除,也就意味着,这个数字是每一个数对中任意一个数的倍数;
求出任意一个数对的所有的倍数,我们需要输出的值就一定在这些倍数里面;所以,暴力枚举的方法是先求出第一个数对中所有的倍数,并放在一个数组里面,然后依次遍历数组,把数组中那个能被所有数对中任意一个数整除的值输出就行了;这个方法的时间复杂度比较难计算,因为我们很难计算出一个数的所有倍数的个数;所以要尽可能的剪一点枝;我们假设一个数对有一个数是18,那么相应的倍数就是 2,3,6,9,18(如果倍数是1就直接输出-1,所以这里算倍数的时候不算1),我们假设6能够被所有数对中任意一个数整除,那么2,3(2*3 = 6)是不是也一定能够被数对中的任意一个数整除;所以算倍数的时候,倍数之间存在倍数关系就应该删去大的那个,减少要遍历倍数的次数;所以在减去倍数之间成倍数关系并且是较大的那个之后,18的倍数就应该是2和3;当要求倍数的数字非常大的时候,这个剪枝就能省下很多时间;这个剪枝要如何实现?这里有一个方法,但是我不会证明个方法的可行性,暂且当理论用吧;这是我们平时求一个数的倍数,效率比较高的方法 for(int i = 2; i*i <= x; ++i)
我们只需要在这个方法上面加上一句代码就行了,具体看下下面的代码;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<sstream>
#include<vector>
#include<string>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0);
int read(){
int r=0,f=1;char p=getchar();
while(p>'9'||p<'0'){if(p=='-')f=-1;p=getchar();}
while(p>='0'&&p<='9'){r=r*10+p-48;p=getchar();}return r*f;
}
typedef long long ll;
const int Maxn = 2e5;
const long long LINF = 1e18;
const int INF = 0x3f3f3f3f;
int x[Maxn],y[Maxn],r[10005],M = 0;
void solve (int x) {
for (int i = 2; i*i <= x; ++i) {
if(x%i == 0) {
r[M++] = i;
while (x%i == 0) x/=i; // 这段代码能够跳过倍数之间存在倍数关系且较大的那个数
}
}
if(x > 1) r[M++] = x;
}
int main (void)
{
// IOS;
int n;
scanf("%d",&n);
for (int i = 0; i < n; ++i) {
scanf("%d%d",&x[i],&y[i]);
}
if(n == 1) {
if(x[0] != 1) printf("%d",x[0]);
else if(y[0] != 1) printf("%d",y[0]);
else printf("-1");
return 0;
}
solve (x[0]); solve (y[0]);
// 这么这两句能够把数组中重复的数字去掉,是标准函数库的函数
sort(r,r+M);
M = unique(r,r+M)-r;
for (int i = 0; i < M; ++i) {
int j = 1;
cout << r[i] << endl;
for (; j < n; ++j) {
if(x[j]%r[i] && y[j]%r[i]) break;
}
if(j == n) {
printf("%d",r[i]);
return 0;
}
}
printf("-1");
return 0;
}
第二种方法:
因为要求的值是一个数对中任意一个数的倍数,那么这两个数对相乘得到的数一定能被我们要求的数整除;所以,把两个数对相乘,合成一个数,求出所有相乘后的数的公约数,这里可以用gcd来求最大公约数,得到的最大公约数如果是 1,就说明不存在一个数能满足题目的条件,如果大于1,要尽可能的把这个公约数化简到最小,因为得到的最大公约数是一个数对相乘得到的结果,所以这个公约数可能会大于一些数对的数,化简的方法就是和所有给出的数对进行gcd的操作;注意: __gcd() 不是标准库的函数;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<sstream>
#include<vector>
#include<string>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0);
#define gcd std::__gcd
int read(){
int r=0,f=1;char p=getchar();
while(p>'9'||p<'0'){if(p=='-')f=-1;p=getchar();}
while(p>='0'&&p<='9'){r=r*10+p-48;p=getchar();}return r*f;
}
typedef long long ll;
const int Maxn = 2e5;
const long long LINF = 1e18;
const int INF = 0x3f3f3f3f;
ll x[Maxn],y[Maxn];
int main (void)
{
// IOS;
ll n,ans = 0;
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> x[i] >> y[i];
ans = gcd(ans,x[i]*y[i]);
}
if(ans == 1) return puts("-1"),0;
for (int i = 0; i < n; ++i) {
if(gcd(ans,x[i]) > 1) ans = gcd(ans,x[i]);
else ans = gcd(ans,y[i]);
}
cout << ans << endl;
return 0;
}