题意:
给出n个整数,对每个整数可以减去0-k的任意一个数
求这样操作后,n个数的最大GCD是多少
分析:
我们首先可以知道n个整数中最小的数是多少
而且,最终的答案肯定不大于这个数
这个n个整数中最小的数是答案的上限
然后对于答案的下限
可以肯定的是
1肯定是答案的下限
2呢?3呢?为什么1一定是
其实,0-k+1,都可以作为答案
为什么?
可以把k想象成一个剪刀
对k+1来说,任何数都可以剪掉0-k变成k+1的倍数(任何数模k+1的结果都是0-k)
所以0-k也可以,综上0-k+1都可以,所以答案的下限是k+1
答案处于[k+1,minn]
如果minn<k+1,那么就是minn
如果minn>k+1,则我们枚举这其中所有的数
看最大的能使得所有数列中的数经过操作之后GCD(all)确实能等于这个数的数是多少
也就是验证这个数的可行性,找出可行的数中最大的那个
怎么验证呢?给出一个数m,怎么才能知道可行不可行呢?
一步步来推理
每个数模m,结果都是0--m-1
对于0--m-1,我们的剪刀最多只能剪掉k
所以如果数列中某个数模m的结果大于k,则m不可行
那么我们是不是能这样做
对数列中的每一个数都模m呢
估计一下复杂度,枚举是100W,最多有30万个数,则100w*30w,T!
转化一下这个问题
我们是对每个数尝试一下它是不是处于可行区间
反过来,如果可行区间有这个数的话,是一样的
如果所有可行区间中存在的数的和是n,则这个m是可行的
可行区间是什么?
一个数模m即是x =m*d+x%m
x%m小于等于k,也就是x处于[m*d,m*d+k]
那么所有的可行区间就是
[m*1,m*1+k],[m*2,m*2+k],[m*3,m*3+k],[m*4,m*4+k]...
有logn个这样的区间
算下复杂度,n*logn
这些区间内出现的数的总和我们可以累加前缀和之差得到
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int NN=555555;
int vistmp[NN*2];
int vis[NN*2];
int f[NN];
int n,k;
int maxn;
bool check(int s){
int sum=0;
for(int i=1;i*s<=maxn;i++){
sum += vis[min(maxn,i*s+k)] - vis[i*s-1];//注意细节
if(s==7){
}
}
return sum==n;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("G:/in.txt","r",stdin);
//freopen("G:/myout.txt","w",stdout);
#endif
cin>>n>>k;
int minn=1<<30;
for(int i=1;i<=n;i++){
cin>>f[i];
minn=min(minn,f[i]);
maxn=max(maxn,f[i]);
vistmp[f[i]]++;
}
vis[minn]=vistmp[minn];
for(int i=minn+1;i<=maxn;i++)
vis[i]=vis[i-1]+vistmp[i];
if(minn<=k+1){
cout<<minn<<endl;
return 0;
}
for(int i=minn;i>=k+1;i--){
if(check(i)){
cout<<i<<endl;
return 0;
}
}
}