【Codeforces Round 276 (Div 2)D】【数论 正难则反 枚举倍数 二分 数组优化O(nlogn)】Maximum Value n个数大数mod小数找最大余数
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=2e5+10,M=2e6+10,Z=1e9+7,ms63=1061109567;
int n;
int a[N],b[M];
int main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
sort(a+1,a+n+1);a[n+1]=a[n]<<1;
for(int i=1;i<=n;++i)
{
for(int j=a[i];j<a[i+1];++j)b[j]=a[i];
}
int ans=0;
for(int i=n-1;i>=1;--i)
{
if(a[i]+1<=ans)break;
if(a[i]==a[i+1])continue;
for(int x=a[i]+a[i]-1;x<a[n+1];x+=a[i])
{
gmax(ans,b[x]%a[i]);
}
}
printf("%d\n",ans);
}
return 0;
}
/*
【trick&&吐槽】
哇哈!尝试优化,暴力剪枝竟然AC啦~~
用错误的做法AC一道题果然是心里最开心的2333
【题意】
给你n(2e5)个数,每个数的数值都在[1,1e6]之间,让你求出max(a[i]%a[j]),a[i]>=a[j]]。
【类型】
数论
【分析】
我们假设这n个数已经按照从小到大的顺序形成了从前到后的排序。
那么——如果没有a[i]>=a[j]的限制,答案肯定是max(a[x]),a[x]!=a[n]
然而,现在有a[i]>=a[j]的要求了。我们怎么做呢?
我当时做的第一种做法,是枚举大的数,然后找小的数,来更新答案,这种算法时间复杂度是O(nsqrt(n)log(n))会TLE。
然而,做题的时候竟然没有想到反过来。
就是我们找素数的时候,枚举数找因子的时间复杂度是O(nsqrt(n))
可是如果转换一下次序,枚举数筛倍数的时间复杂度就只有O(nlog(n))了。
这题也一样。
1,枚举因子找倍数,对于一个因子的一个倍数,我们找比这个倍数小的最大的数(通过lower_bound()-1实现)
2,从大到小枚举做剪枝
这题就在O(nlogn(n)log(n))的时间做完啦。
此外,先可以贪心x(x/2+1)为ans,可以起到优化作用。
PS:
因为a[]的数值并不大。
所以,我们可以用另外一种做法,直接记录<=x的数中最大的数为b[x]。然后我们就可以O(1)查询。
就可以达到整体上O(nlogn)的时间复杂度啦。啦啦啦啦~
【时间复杂度&&优化】
O(nlogn logn)->O(nlogn)
*/
【Codeforces Round 276 (Div 2)D】【数论 正难则反 枚举倍数 二分】Maximum Value n个数大数mod小数找最大余数
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=2e5+10,M=1e6+10,Z=1e9+7,ms63=1061109567;
int n;
int a[N];
bool e[M];
int main()
{
while(~scanf("%d",&n))
{
MS(e,0);
for(int i=1;i<=n;++i){scanf("%d",&a[i]);e[a[i]]=1;}
sort(a+1,a+n+1);a[n+1]=1e9;
int ans=0;
for(int i=n;i>1;--i)if(e[a[i]]&&e[a[i]/2+1])gmax(ans,a[i]%(a[i]/2+1));
for(int i=n-1;i>=1;--i)
{
if(a[i]+1<=ans)break;
if(a[i]==a[i+1])continue;
int p=i;
while(1)
{
int x=(a[p]/a[i]+1)*a[i];
p=lower_bound(a+p,a+n+1,x)-a-1;
gmax(ans,a[p]%a[i]);
if(++p>n)break;
}
}
printf("%d\n",ans);
}
return 0;
}
/*
【trick&&吐槽】
哇哈!尝试优化,暴力剪枝竟然AC啦~~
用错误的做法AC一道题果然是心里最开心的2333
【题意】
给你n(2e5)个数,每个数的数值都在[1,1e6]之间,让你求出max(a[i]%a[j]),a[i]>=a[j]]。
【类型】
数论
【分析】
我们假设这n个数已经按照从小到大的顺序形成了从前到后的排序。
那么——如果没有a[i]>=a[j]的限制,答案肯定是max(a[x]),a[x]!=a[n]
然而,现在有a[i]>=a[j]的要求了。我们怎么做呢?
我当时做的第一种做法,是枚举大的数,然后找小的数,来更新答案,这种算法时间复杂度是O(nsqrt(n)log(n))会TLE。
然而,做题的时候竟然没有想到反过来。
就是我们找素数的时候,枚举数找因子的时间复杂度是O(nsqrt(n))
可是如果转换一下次序,枚举数筛倍数的时间复杂度就只有O(nlog(n))了。
这题也一样。
1,枚举因子找倍数,对于一个因子的一个倍数,我们找比这个倍数小的最大的数(通过lower_bound()-1实现)
2,从大到小枚举做剪枝
这题就在O(nlogn(n)log(n))的时间做完啦。
此外,先可以贪心x(x/2+1)为ans,可以起到优化作用。
PS:
因为a[]的数值并不大。
所以,我们可以用另外一种做法,直接记录<=x的数中最大的数为v[x]。然后我们就可以O(1)查询。
就可以达到整体上O(nlogn)的时间复杂度啦。啦啦啦啦~
【时间复杂度&&优化】
O(nlogn logn)->O(nlogn)
*/