LibreOJ #6190. 序列查询(线段树+剪枝)

Posted Sakits

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LibreOJ #6190. 序列查询(线段树+剪枝)相关的知识,希望对你有一定的参考价值。

  莫队貌似是过不了的,这题是我没见过的科技...

  首先区间按右端点排序,然后一个扫描线,扫到某个区间右端点时候计算答案,线段树上节点的信息并不需要明确定义,我们只要求线段树做到当前扫到now时,查询[L,now]即为这一段的答案。

  朴素的不加优化的做法,我们在每一个点R加进来的时候要更新1~R-1所有点,这样显然是会TLE的。

  强调一遍我们只要求线段树做到当前扫到now时,查询[L,now]即为这一段的答案,因此我们记录一下更新到现在的最小的绝对值mn,对于线段树上每一节点都维护一个set,维护这个节点代表的区间里的数有序,每次新加进来一个数,我们直接查询他的前驱后继,算出绝对值后与mn比较,若是大于mn显然这个区间就不用更新了。为什么?再强调一遍线段树上节点的信息并不需要明确定义,我们只要求线段树做到当前扫到now时,查询[L,now]即为这一段的答案。

  这样一个类似最优性剪枝之后的东西能保证复杂度吗?实际上是可以的。

  至少在这题上,最坏的情况是一个这样的序列: 1 n n/2 n/4 n/8 n/16,也就是对于每一个位置顶多被递归到叶子logn次,也就是最坏复杂度O(NlogNlogNlogN),但实际上是到不了这个复杂度的...也就可以过了

  思考与扩展:这样一个东西可以用在离线处理一个区间某种信息的最值上

  感谢栋栋的悉心教导我这样一个大傻逼QAQ

技术分享
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#define ll long long 
using namespace std;
const int maxn=100010,inf=2147483647;
struct poi{int l,r,pos;}q[maxn*3];
int n,m,x,y,z,tot,mn;
int tree[maxn<<2],a[maxn*3],ans[maxn*3];
set<int>s[maxn<<2];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<0||c>9)c==-&&(f=-1),c=getchar();
    while(c<=9&&c>=0)k=k*10+c-0,c=getchar();
    k*=f;
}
inline int abs(int x){return x>=0?x:-x;}
inline int min(int a,int b){return a>b?b:a;}
void build(int x,int l,int r)
{
    tree[x]=inf;if(l==r)return;
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
}
void update(int x,int l,int r,int cx,int delta)
{
    if(l==r){if(l==cx)return;tree[x]=min(tree[x],abs(a[l]-delta));mn=min(mn,tree[x]);return;}
    if(r<=cx)
    {
        set<int>::iterator it=s[x].lower_bound(delta);
        if((it==s[x].end()||abs(*it-delta)>=mn)&&(it==s[x].begin()||abs(*(--it)-delta)>=mn))
        {
            mn=min(mn,tree[x]);
            return;
        }
        int mid=(l+r)>>1;
        update(x<<1|1,mid+1,r,cx,delta);
        update(x<<1,l,mid,cx,delta);
        tree[x]=min(tree[x<<1],tree[x<<1|1]);
        return;
    }
    int mid=(l+r)>>1;
    if(cx<=mid)update(x<<1,l,mid,cx,delta);
    else update(x<<1|1,mid+1,r,cx,delta),update(x<<1,l,mid,cx,delta);
    tree[x]=min(tree[x<<1],tree[x<<1|1]);
}
void pushset(int x,int l,int r,int cx,int delta)
{
    s[x].insert(delta);
    if(l==r)return;
    int mid=(l+r)>>1;
    if(cx<=mid)pushset(x<<1,l,mid,cx,delta);
    else pushset(x<<1|1,mid+1,r,cx,delta);
}
int query(int x,int l,int r,int cl,int cr)
{
    if(cl<=l&&r<=cr)return tree[x];
    int mid=(l+r)>>1,ret=inf;
    if(cl<=mid)ret=min(ret,query(x<<1,l,mid,cl,cr));
    if(cr>mid)ret=min(ret,query(x<<1|1,mid+1,r,cl,cr));
    return ret;
}
bool cmp(poi a,poi b){return a.r<b.r;}
int main()
{
    read(n);
    for(int i=1;i<=n;i++)read(a[i]);build(1,1,n);
    read(m);
    for(int i=1;i<=m;i++)read(q[i].l),read(q[i].r),q[i].pos=i;
    sort(q+1,q+1+m,cmp);
    for(int i=1,j=1;i<=m;i++)
    {
        for(;j<=q[i].r;j++)
        {
            mn=inf;
            update(1,1,n,j,a[j]);
            pushset(1,1,n,j,a[j]);
        }
        if(q[i].l==q[i].r)ans[q[i].pos]=inf;
        else ans[q[i].pos]=query(1,1,n,q[i].l,q[i].r-1);
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}
View Code

以上是关于LibreOJ #6190. 序列查询(线段树+剪枝)的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ #139 树链剖分 [树链剖分,线段树]

LibreOJ #2016. 「SCOI2016」美味

LibreOJ #2013. 「SCOI2016」幸运数字

权值线段树套序列线段树

BZOJ 1858 SCOI2010 序列操作 线段树

LibreOJ #6208. 树上询问