题目链接:https://codeforces.com/problemset/problem/484/B
题意:给一个长度为n的序列,求满足
a
i
>
a
j
a_i>a_j
ai>aj的最大的
a
i
%
a
j
a_i\%a_j
ai%aj
思路:首先想到O(n^2)的暴力,考虑如何优化,对于一个
a
i
a_i
ai:
a
i
=
a
i
%
a
j
+
k
∗
a
j
a_i=a_i\%a_j+k*a_j
ai=ai%aj+k∗aj
根据这个式子,我们可以将对
a
i
a_i
ai的枚举转为对
a
j
a_j
aj倍数的枚举,对于每个区间
[
k
∗
a
j
,
(
k
+
1
)
∗
a
j
)
[k*a_j,(k+1)*a_j)
[k∗aj,(k+1)∗aj),我们找到其中的最大值。
代码:
根据找最大值方法不同,写了几种做法:
1.线段树找最大值
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7,M=1e6+7;
int a[N],tag[M];
struct node{
int l,r;
int ma,f;
}tr[M<<2];
node merge(node nl,node nr)
{
node tmp;
tmp.l=nl.l;tmp.r=nr.r;
if(nl.f!=0&&nr.f!=0) tmp.f=1,tmp.ma=max(nl.ma,nr.ma);
else if(nl.f!=0) tmp.f=1,tmp.ma=nl.ma;
else if(nr.f!=0) tmp.f=1,tmp.ma=nr.ma;
else tmp.f=0;
return tmp;
}
void build(int l,int r,int rt)
{
tr[rt].l=l;tr[rt].r=r;
if(l==r)
{
tr[rt].ma=l;
tr[rt].f=tag[l];
return ;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
tr[rt]=merge(tr[rt<<1],tr[rt<<1|1]);
}
node query(int l,int r,int rt)
{
if(tr[rt].l>=l&&tr[rt].r<=r) return tr[rt];
if(tr[rt<<1].r>=r) return query(l,r,rt<<1);
if(tr[rt<<1|1].l<=l) return query(l,r,rt<<1|1);
return merge(query(l,r,rt<<1),query(l,r,rt<<1|1));
}
int main()
{
int n;
scanf("%d",&n);
int up=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
tag[a[i]]++;
up=max(up,a[i]);
}
// if(n==200000&&a[1]==13) printf("%d\n",up);
// sort(a+1,a+1+n);
build(1,up,1);
int ans=0;
for(int i=up;i>=2;i--)
{
if(tag[i]==0) continue;
if(i<=ans) continue;
for(int k=1;k*i<=up;k++)
{
node tmp=query(k*i,min((k+1)*i-1,up),1);
if(tmp.f) ans=max(ans,tmp.ma%i);
}
}
printf("%d\n",ans);
}
2.ST表找
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7,M=1e6+7;
int a[N],tag[M];
int st[M][35];//st表
int lg[M];
void initST(int n)
{
for (int i = 1; i <= n; i++){
st[i][0] = tag[i]?i:0;
}
lg[1] = 0;
for(int i = 2; i <= n; i++)
lg[i] = lg[i-1] + (1 << (lg[i-1] + 1)== i);
for (int j = 1; (1 << j) <= n; j++){
for (int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = max(st[i][j - 1],st[i + (1 << (j - 1))][j - 1]);
}
}
int search(int l, int r)
{
int k = lg[r - l + 1];
return max(st[l][k],st[r - (1 << k) + 1][k]);
}
int main()
{
int n;
scanf("%d",&n);
int up=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
tag[a[i]]=1;
up=max(up,a[i]);
}
sort(a+1,a+1+n);
n=unique(a+1,a+1+n)-a-1;
// for(int i=1;i<=n;i++) printf("%d\n",a[i]);
initST(up);
int ans=0;
for(int i=n;i>=1;i--)
{
// if(a[i]<=ans) continue;
for(int k=a[i];k<=up;k+=a[i])
{
int tmp=search(k,min(k+a[i]-1,up));
// printf("tmp:%d\n",tmp);
ans=max(ans,tmp%a[i]);
}
}
printf("%d\n",ans);
}
3.二分找
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7,M=1e6+7;
int a[N],tag[M];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
sort(a+1,a+1+n);
n=unique(a+1,a+1+n)-a-1;
a[n+1]=0x3f3f3f3f;
int ans=0;
for(int i=n;i>=1;i--)
{
if(a[i]<=ans) continue;
for(int j=2*a[i];j<=2*a[n];j+=a[i])
{
int x=lower_bound(a+1,a+2+n,j)-a-1;
ans=max(ans,a[x]%a[i]);
}
}
printf("%d\n",ans);
}
4.遍历处理每个数距离它最近的最大值
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7,M=4e6+7;
int a[N],tag[M],b[M];
int main()
{
int n;
scanf("%d",&n);
int up=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
tag[a[i]]=1;
up=max(up,a[i]);
}
for(int i=1;i<=2*up;i++)
{
b[i]=b[i-1];
if(tag[i-1]==1) b[i]=i-1;
}
int ans=0;
for(int i=up;i>=2;i--)
{
if(tag[i]==0) continue;
if(i<=ans) continue;
for(int j=2*i;j<=2*up;j+=i) ans=max(ans,b[j]+i-j);
}
printf("%d\n",ans);
}