【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) */