• 2019HDU多校第四场 K-th Closest Distance ——主席树&&二分


    题意

    给定 $n$ 个数,接下来有 $q$ 次询问,每个询问的 $l, r, p, k$ 要异或上一次的答案,才是真正的值(也就是强制在线)。每次询问,输出 $[l, r]$ 内第 $k$ 小的 $|p-a[i]|$.

    分析

    通常主席树用来求区间第K大,其实它的实际作用是统计某个区间内值的个数。所以,

    对于每次询问,对答案进行二分,对于可能的答案 $x$,对 $R_l sim  R_r$ 的线段树查找 $[p-x, p+x]$ 的是否为 $k$.

    主席树中在值上建立的,这题数据范围为 $10^6$,不需要离散化(话说强制在线的离散化我也不会

    感觉有些卡常,32倍、55倍的空间都TLE,改成64倍就过了(为啥啊)

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e5 + 100;
    int n, m;
    //int a[maxn];
    //int rt[maxn], lc[maxn << 5], rc[maxn << 5], sum[maxn << 5];  //rt:不同版本的根节点   lc/rc: 左儿子、右儿子(公用)  sum: 和(公用)
    int rt[maxn], lc[maxn*64], rc[maxn*64], sum[maxn*64];
    int node_cnt;    //node总计数, pnt_disc: A中数字对应B中的值
    int range = 1000000;  //数据范围,也就是线段树的大小
    
    void build(int& last_node, int l, int r)
    {
        last_node = ++ node_cnt;
        sum[last_node] = 0;
        if(l == r)  return;
        int mid = (l + r) >> 1;
        build(lc[last_node], l, mid);
        build(rc[last_node], mid+1, r);
    }
    
    
    int modify(int pre_rt, int v, int l, int r)
    {
        int new_rt = ++node_cnt;
        lc[new_rt] = lc[pre_rt];
        rc[new_rt] = rc[pre_rt];
        sum[new_rt] = sum[pre_rt] + 1;
    
        int mid = (l + r) >> 1;
        if(l == r)  return new_rt;
        if(mid >= v)  lc[new_rt] = modify(lc[new_rt],v, l, mid);
        else  rc[new_rt] = modify(rc[new_rt], v, mid+1, r);
        return new_rt;
    }
    
    //查询[ql, qr]中不同元素个数
    int query(int rt1, int rt2, int ql, int qr, int l, int r)
    {
        //printf("rt1:%d  rt2:%d  k:%d  l:%d  r:%d  ", rt1, rt2, k, l, r);
        if(ql <= l && r <= qr)  return sum[rt2]-sum[rt1];
    
        int mid = (l + r) >> 1;
        int ans = 0;
        if(ql <= mid)  ans += query(lc[rt1], lc[rt2], ql, qr, l, mid);
        if(qr > mid) ans += query(rc[rt1], rc[rt2], ql, qr, mid+1, r);
        return ans;
    }
    
    void print_debug()
    {
        printf("node_cnt: %d
    ", node_cnt);
        for(int i = 0;i <= node_cnt;i++)
            printf("%d  lc:%d  rc:%d  sum:%d
    ", i, lc[i], rc[i], sum[i]);
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d%d", &n, &m);
    
            node_cnt = 0;
            build(rt[0], 1, range);
            for(int i = 1;i <= n;i++)
            {
                int tmp;
                scanf("%d", &tmp);
                rt[i] = modify(rt[i-1], tmp, 1, range);  //只在上一个版本的基础上修改
            }
    
            int ans = 0;
            for(int i = 0;i <m;i++)
            {
                int l, r, p, k;
                scanf("%d%d%d%d", &l, &r, &p, &k);
                l ^= ans; r ^= ans;
                p ^= ans; k ^= ans;
                if(l > r)  swap(l, r);
    
                int L = 0, R = range;      //
                while(L <= R)
                {
                    int M = L + (R-L)/2;
                    if(query(rt[l-1], rt[r], max(1, p-M), min(p+M, range), 1, range) >= k)
                    {
                        ans = M;
                        R = M-1;
                    }
                    else  L = M+1;
                }
                printf("%d
    ", ans);
            }
        }
    }
    [ Copy to Clipboard ]    [ Save to File]

    参考链接:

    1. https://blog.csdn.net/birdmanqin/article/details/97964662

    2. http://morecoder.com/article/1254619.html

  • 相关阅读:
    IOC理论推导
    spring leile
    缓存
    动态SQL
    canvas小球运动
    jdk1.7后字符串的总结
    用ssm框架简单创建一个增删改查案列
    京东物流居家品类各区域联系人
    京东网址收藏
    京东自营申请新品打标方法
  • 原文地址:https://www.cnblogs.com/lfri/p/11289552.html
Copyright © 2020-2023  润新知