MDZZ

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值