zoukankan      html  css  js  c++  java
  • 牛客练习赛53 E-老瞎眼pk小鲜肉(思维+线段树+离线)

    前言


    听说是线段树离线查询??

    做题做着做着慢慢对离线操作有点感觉了,不过也还没参透,等再做些题目再来讨论离线、在线操作。

    这题赛后看代码发现有人用的树状数组,$tql$。当然能用树状数组写的线段树也能写,最重要的还是思维上面。

    我当时是怎么想来着。一看异或和前缀和不是很像么,我先把数组中所有数字连续异或并存起来,就像处理前缀和一样的。之后对于每组查询,我只要找到区间中相等的数就可以了,然后再取这些数中相隔最近的距离输出即可。想法很好,但是现实很骨感,这样一来每个区间都这样处理不就超时了么,遂放弃。

     题意


    给出一段长度为$n$的区间,$q$次询问求$Lsim R$这个区间内最短的一段区间$lsim r$使得$oplus_{i=l}^{r} a_j= 0$其中$Lleq l<rleq r$

    分析


    首先预处理出对于每个位置$i$找到左边离$i$最近的位置$j$使得$a[j] wedge a[j+1] wedge a[j+2]wedge  …a[i]=0$,并记作$pre[i]$。

    将询问按$r$从小到大排序,然后从$1sim n$处理每个位置,对于位置$i$,在线段树上$pre[i]$更新为$i-pre[i]+1$($i$与离$i$右边最近满足$oplus_{i=l}^{r} a_j= 0$的$r$间的距离),然后查询$(Q[i].l, Q[i].r)$的最小值即可 $[link]$

    Code

    #include <cstdio>
    #include <algorithm>
    #define lson rt<<1, l , mid
    #define rson rt<<1|1, mid+1 , r
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn = 5e5+6;
    int n, q;
    int pos[maxn<<2], pre[maxn], tree[maxn<<2], ans[maxn];
    struct node{
        int l,r,id;
        bool operator<(const node x)const{
            return r<x.r;
        }
    }Q[maxn];
    void push_up(int rt){
        tree[rt] = min(tree[rt<<1], tree[rt<<1|1]);
    }
    void update(int rt,int l,int r,int pos,int val){
        if(l==r){
            tree[rt] = min(tree[rt], val);
            return;
        }
        int mid = (l+r)>>1;
        if(pos<=mid) update(lson, pos, val);
        else update(rson, pos, val);
        push_up(rt);
    }
    int query(int rt, int l, int r, int ul, int ur){
        if(ul<=l&&r<=ur) return tree[rt];
        int mid = (l+r)>>1, ans = INF;
        if(ur<=mid) return query(lson, ul, ur);
        else if(ul>mid) return query(rson, ul, ur);
        else return min(query(lson, ul, ur),query(rson, ul, ur));
    }
    /*这种写法也可以,但是就我以前做过的一道题目来说上面那种更快(不知道为啥,还有待考证)
    int query(int rt, int l, int r, int ul, int ur){
        if(ul<=l&&r<=ur) return tree[rt];
        int mid = (l+r)>>1, ans = INF;
        if(ul<=mid) ans = min(ans, query(lson, ul, ur));
        if(ur>mid) ans = min(ans, query(rson, ul, ur));
        return ans;
    }
    */
    int main(){
        scanf("%d%d", &n, &q);
        for(int i = 1;i <= 4*500000; i++){
            pos[i] = -1;
            tree[i] = INF;
        }
        int sum = 0; pos[0] = 0;
        for(int i = 1,x; i <= n; i++){
            scanf("%d",&x);
            sum ^= x;
            if(pos[sum]!=-1) pre[i] = pos[sum]+1;
            else pre[i] = -1;
            pos[sum] = i;
        }
        for(int i = 1; i <= q; i++){
            scanf("%d%d", &Q[i].l, &Q[i].r);
            Q[i].id = i;
        }
        sort(Q+1, Q+1+q);
        int cnt = 1;
        for(int i = 1; i <= n; i++){
            if(pre[i]!=-1) update(1, 1, n, pre[i], i-pre[i]+1);
            while(Q[cnt].r==i){
                ans[Q[cnt].id] = query(1, 1, n, Q[cnt].l, Q[cnt].r);
                cnt++;
            }
        }
        for(int i = 1; i <= q; i++){
            printf("%d
    ", ans[i]==INF?-1:ans[i]);
        }
        return 0;
    }
    View Code

    思考


    说实话现在也只是知道了这题这么做是对的,但是自己很难会想到这个方法,可能要多做些题目才知道吧,据说和HH的项链很像,有时间就去看下这题。

    还有就是我看这题代码的时候有个想法就是,能不能把$update(1, 1, n, pre[i], i-pre[i]+1)$换成$update(1, 1, n, i, i-pre[i]+1)$。当时觉得这样应该是差不多的,然而$WA$。

    仔细想想后,发现用$pre[i]$的位置记录线段长度是有原因的,并非$pre[i]$和$i$都可以。如果用$pre[i]$也即是左端点记录线段长度的话,对于每组查询而言,区间内每个满足条件的点,它所代表的线段的右端点都不会超过当前的$r$,因此可以用来直接求最小值。但是!!假如是用$i$也即是右端点来记录线段长度的话,区间内每个满足条件的点,它所代表的线段的左端点都可能会超过当前的$l$,那个点就不能用来求最小值,但很显然计算的时候会把这个点计入,所以这样得到的结果可能就是错的!!


    参考博客:

    https://www.cnblogs.com/zjl192628928/p/11661714.html

  • 相关阅读:
    glog入门demo
    gflag的简单入门demo
    caffe库源码剖析——net层
    排序算法的c++实现——计数排序
    docker的/var/lib/docker目录迁移
    SpringCloud Ribbon 负载均衡 通过服务器名无法连接的神坑一个
    Spring Boot Cache使用与整合
    Navicat Keygen
    Windows / Office
    docker swarm 搭建与服务更新
  • 原文地址:https://www.cnblogs.com/wizarderror/p/11672644.html
Copyright © 2011-2022 走看看