ZJOI2013 K大数查询

题目链接

Solution

首先另 L = -n, R = n,那么 mid = (L + R) >> 1.考虑对于 mid 把所有操作(修改 / 查询)分为两类:dl 和 dr,使得两部分互不干扰。这样就可以把原问题拆成两个子问题,从而进行整体二分。

考虑维护一个线段树,表示 [l,r] 区间有多少个数 > mid;

对于修改操作:

如果 c > mid,在线段树上对 [l,r] 整体 +1;这时候该操作可能对右区间产生贡献,因此放入 dr 内;

如果 c <= mid,该操作可能对左区间产生贡献,因此放入 dl 内。

对于查询操作:

在线段树上查询 [l,r] 内有几个数 > mid,记为 res:

如果 c > res,说明该询问的答案应该 < mid,因此把当前询问分入 dl 内;

否则,说明当前询问的答案应该 >= mid,因此把当前询问放入 dr 内。

总结一下,线段树需要支持如下两个操作:区间 +1,区间求和。

其他一些具体细节在代码里(代码是抄的题解 qwq)

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;

const LL N = 2333333;
struct Que { LL opt, l, r, c, id; } q[N], dl[N], dr[N];
LL n, m, num = 0, len1 = 0, len2 = 0, ql[N], qr[N], ans[N];

struct SegmentTree
{
    LL tag[N], sum[N];
    void push_up(LL x) { sum[x] = sum[x << 1] + sum[x << 1 | 1]; }
    void push_down(LL x, LL l, LL r)
    {
        if(tag[x] == 0) return ;
        LL mid = (l + r) >> 1;
        sum[x << 1] += tag[x] * (mid - l + 1);
        sum[x << 1 | 1] += tag[x] * (r - mid);
        tag[x << 1] += tag[x], tag[x << 1 | 1] += tag[x];
        tag[x] = 0;
    }
    void update(LL x, LL l, LL r, LL stdl, LL stdr, LL k)
    {
        if(l > stdr || r < stdl) return ;
        if(stdl <= l && stdr >= r)
        {
            tag[x] += k;
            sum[x] += k * (r - l + 1);
            push_down(x, l, r);
            return ;
        }
        LL mid = (l + r) >> 1;
        push_down(x, l, r);
        update(x << 1, l, mid, stdl, stdr, k);
        update(x << 1 | 1, mid + 1, r, stdl, stdr, k);
        push_up(x), push_down(x, l, r);
    }
    LL query(LL x, LL l, LL r, LL stdl, LL stdr)
    {
        if(l > stdr || r < stdl) return 0;
        if(stdl <= l && stdr >= r) return sum[x];
        LL mid = (l + r) >> 1;
        push_down(x, l, r);
        return query(x << 1, l, mid, stdl, stdr) + 
        query(x << 1 | 1, mid + 1, r, stdl, stdr);
    }
} tree;

void work(LL st, LL ed, LL l, LL r)
{
    LL lenl = 0, lenr = 0, Fl = 0, Fr = 0;
    if(l == r)
    {
        for(LL i = st; i <= ed; i++)
            if(q[i].opt == 2) ans[q[i].id] = l;
        return ;
    }
    LL mid = (l + r) >> 1;
    for(LL i = st; i <= ed; i++)
    {
        if(q[i].opt == 1)
            if(q[i].c > mid)
            {
                tree.update(1, 1, n, q[i].l, q[i].r, 1);
                dr[++lenr] = q[i];
            }
            else dl[++lenl] = q[i];
        else
        {
            LL res = tree.query(1, 1, n, q[i].l, q[i].r);
            if(res >= q[i].c) Fr = 1, dr[++lenr] = q[i];
            else
            {
                Fl = 1, q[i].c -= res;
                dl[++lenl] = q[i];
            }
        }
    }
    for(LL i = st; i <= ed; i++)
        if(q[i].opt == 1 && q[i].c > mid)
            tree.update(1, 1, n, q[i].l, q[i].r, -1);
    for(LL i = 1; i <= lenl; i++) q[st + i - 1] = dl[i];
    for(LL i = lenl + 1; i <= lenl + lenr; i++) q[st + i - 1] = dr[i - lenl];
    if(Fl == 1) work(st, st + lenl - 1, l, mid);
    if(Fr == 1) work(st + lenl, ed, mid + 1, r);
} 

int main()
{
    scanf("%lld%lld", &n, &m);
    for(LL i = 1; i <= m; i++)
    {
        scanf("%lld%lld%lld%lld", &q[i].opt, &q[i].l, &q[i].r, &q[i].c);
        if(q[i].opt == 2) q[i].id = ++num;
    }
    work(1, m, -n, n);
    for(LL i = 1; i <= num; i++) printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2020-10-05 21:27  Nyxia  阅读(136)  评论(0编辑  收藏  举报