100个case 给一个大小是n,1<=ai<=n的数组
先贴一发大佬教我的做法,orz~
求max(ai%aj) i小于j
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <stack>
#include <set>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const LL INF = 1LL<<60;
const int mod = 1000000007;
const int N = 5005;
int Max = 0;
int pre[100005];
int a[50005];
int n;
int ans = 0;
void work(){
memset(pre,0,sizeof pre);
Max = 0;
ans = 0;
scanf("%d",&n);
for(int i = 0; i < n; i++){
scanf("%d",&a[i]);
pre[a[i]] = a[i];
}
for(int i = n-1; i >= 0; i--){
if(a[i] < Max&&a[i] > ans){
ans = a[i];
}
if(a[i] > Max)Max = a[i];
}/*先判断a[i]是否可以成为一个ans*/
for(int i = 1; i <= Max*2; i++){
if(pre[i]==0){
pre[i] = pre[i-1];
}
}/*pre[i]代表<=i的最大值*/
for(int i = 1; i <= Max; i++){
if(pre[i]==i){//模i
for(int j = i; j <= Max; j+=i){
ans = max(ans,pre[j+i-1]-j);//寻找距离k*i-1最大的值,-j就是取模了。
}
}
}
printf("%d\n",ans);
}
int main() {
#ifdef local
freopen("in", "r", stdin);
#endif
int T;
scanf("%d",&T);
while(T--){
work();
}
}
起初纠结过为什么这样可以忽略顺序,后来发现假设x%i是一个最优解且x在数组中的位置>i,这样可能会影响答案的正确性,这样会有两种情况1.x的值>i,2.x<=i
第一种如果x>i,那么必然存在一个答案i是合理的解,而第一个循环已经解决了这个问题,而且x%i的解肯定是小于i的,此时i>(x%i)不影响解的正确性
第二种则是x<=i,如果x%i是最优解,那么max( pre(k*i-1) )肯定是x,(x逼近于k*i-1这个位置,k>1),但是x<=i,说明i更逼近于k*i-1,与假设不符,所以不会出现这种情况
画个数轴
x i 2*i-1 3*i-1 4*i-1
举个例子
2
4 3
起初i是3,这时逼近3*2-1=5的是4,所以x = 4,ans =1;
i是4的时候,逼近4*2-1=7的是4,所以x=4,ans=max(ans,4%4)=1;
复杂度T*nlogn
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define fi first
#define se second
using namespace std;
const int maxn=50000+5;
int a[maxn],b[maxn];
struct tree
{
int l,r;
int d;
} Tree[maxn*4];
void build(int l,int r,int k)
{
Tree[k].d = 0;
Tree[k].l = l;
Tree[k].r = r;
if(l == r)
{
return ;
}
int m = (l + r)>>1;
build(l,m,k<<1);
build(m+1,r,k<<1|1);
}
void update(int L,int R,int k,int x)
{
if(L == R && R == x)
{
Tree[k].d = x;
return ;
}
int m = (L + R)>>1;
if(x <= m)
update(L,m,k<<1,x);
else
update(m+1,R,k<<1|1,x);
Tree[k].d = max(Tree[k<<1].d,Tree[k<<1|1].d);
}
int query(int L,int R,int l,int r,int k)
{
if(l <= L && r >= R)
{
return Tree[k].d;
}
int m = (L + R)>>1,ans = 0;
//printf("%d %d %d %d\n",L,R,l,r);
//getchar();
if(l <= m) ans = max(ans,query(L,m,l,r,k<<1));
if(r > m) ans = max(ans,query(m+1,R,l,r,k<<1|1));
return ans;
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int T;
scanf("%d",&T);
while(T--)
{
memset(b,0,sizeof(b));
int n;
int ans = -1;
scanf("%d",&n);
for(int i = 0; i < n; i++)
{
scanf("%d",&a[i]);
b[a[i]] = i;
}
build(0,n,1);
//update(0,n,1,a[0]);
for(int i = 0 ; i < n ; i++)
{
int x = a[i];
if(i == b[x])//必须要有,没有的话多个1就可以卡死吧...
for(int j = 0 ; j * x <= n ; j++)
ans = max(ans,query(0,n,j*x,min(j*x+x-1,n),1)%x);
update(0,n,1,x);
}
printf("%d\n",ans);
}
return 0;
}
啊啊我的天还是这个好理解,虽然代码量比较大
对于一个aj,在(a1—a(j-1)) %aj最优的时候,选择最后一个aj比较合适,这样可以优化大量重复的数据
对一个aj,可以扫描[k*aj,(k+1)*aj-1]的n/aj个区间,扫描各个区间的最大值%aj,一直更新就好了,查询区间最大值用线段树就行
扫描区间的复杂度为n*(1+1/2+1/3+…+1/n) = nlog n,查询的操作为log n
所以总复杂度为T*nlog n*log n