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\n", node_cnt);
    for(int i = 0;i <= node_cnt;i++)
        printf("%d  lc:%d  rc:%d  sum:%d\n", 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\n", ans);
        }
    }
}
[ Copy to Clipboard ]    [ Save to File]

参考链接:

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

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

猜你喜欢

转载自www.cnblogs.com/lfri/p/11289552.html