jzoj 100036 随机

题目

这里写图片描述这里写图片描述


题解

–谁知道这道水题O(n^2)暴力有90分,当然是要动脑筋,我就不讲了
谁又知道这道题要用权值线段树维护,来拿最后那十分
(代码量 * 10 == 分数 * 10/9)QAQ

首先权值线段树就是把原来存下标的 l 和 r 改成了存数值大小
所以说,左子树一定 < 右子树的值

对于这道题呢
我们要维护:
答案(就是相减最小绝对值和长度,取最大值)
最小值
最大值
与他权值相同的数量(合并到一个点)

然后就是子树向根的转移:
1. 对于答案:
继承左儿子
继承右儿子
右儿子的最小值 - 左儿子的最大值
2. 对于最小(大)值:
继承左儿子
继承右儿子

其实,具体方法就是:
如果目前区间[l,r]差的最小值<区间长度,那么我们可以r++,可能差的最小值就会进一步减小
但是如果区间长度>=差的最小值,现在r++是没有意义的(答案不可能再减小了),所以我们就l++

权值线段树的目的就是快速的找到加入一个被移入目标区间的点后,与他相邻最近的点的绝对值之差(可能作为新的最小值)
还有快速的删去一个被移出的点对答案的贡献

最后还可以离散化一下,去个重,加个速

复杂度应该是O(nlogn)的吧


代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1e6+5;

int n;
int ans=0x3f3f3f3f;

struct hihi{
    int x;
    int y;
}a[MAXN];
int b[MAXN],tot,c[MAXN];

struct hehe{
    int minn;
    int maxx;
    int size;
    int ans;
}tree[MAXN*4];

bool comp(const hihi &a,const hihi &b){
    return a.x<b.x;
}

void lisan(){
    for(int i=1;i<=n;i++){
        if(a[i].x!=a[i-1].x)
            tot++;
        b[tot]=a[i].x;
        c[a[i].y]=tot;
    }
}

void add(int i,int x,int y,int z,int size){
    if(x==y){
        tree[i].size+=size;
        if(tree[i].size){
            tree[i].minn=x;
            tree[i].maxx=y;
        }
        else{
            tree[i].minn=0x3f3f3f3f;
            tree[i].maxx=0;
        }
        if(tree[i].size>2)
            tree[i].ans=0;
        else
            tree[i].ans=0x3f3f3f3f;
    }
    else{
        int l=2*i,r=l+1;
        int mid=(x+y)/2;
        if(z<=mid)
            add(l,x,mid,z,size);
        else
            add(r,mid+1,y,z,size);
        tree[i].ans=min(tree[l].ans,tree[r].ans);
        if(tree[l].maxx&&tree[r].minn!=0x3f3f3f3f)
            tree[i].ans=min(tree[i].ans,b[tree[r].minn]-b[tree[l].maxx]);
        tree[i].minn=min(tree[l].minn,tree[r].minn);
        tree[i].maxx=max(tree[l].maxx,tree[r].maxx);
    }
}

int main(){
    freopen("random.in","r",stdin);
    freopen("random.out","w",stdout);
    cin>>n;
    if(!n)
        return 0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].x);
        a[i].y=i;
    }
    sort(a+1,a+1+n,comp);
    lisan();
    for(int i=1;i<=4*n;i++){
        tree[i].ans=0x3f3f3f3f;
        tree[i].size=0;
        tree[i].minn=0x3f3f3f3f;
        tree[i].maxx=0;
    }
    int l=1,r=2;
    add(1,1,tot,c[l],1);
    add(1,1,tot,c[r],1);
    while(r<=n){
        ans=min(ans,max(tree[1].ans,r-l+1));
        if(l+1==r||tree[1].ans>r-l+1){
            if(r==n)
                break;
            r++;
            add(1,1,tot,c[r],1);
        }
        else{
            add(1,1,tot,c[l],-1);
            l++;
        }
    }
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值