Codeforces Round #276 (Div. 1) B. Maximum Value

题目链接: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+kaj
根据这个式子,我们可以将对 a i a_i ai的枚举转为对 a j a_j aj倍数的枚举,对于每个区间 [ k ∗ a j , ( k + 1 ) ∗ a j ) [k*a_j,(k+1)*a_j) [kaj,(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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值