Codecs 4360 祈愿Supplication

本文探讨了一个多组询问场景下的算法问题,即在动态更新和查询一个大型数据集中第K大的元素。通过分析不同解决方法的时间和空间复杂度,文章最终选择了权值线段树套区间树作为最优解决方案。该方法不仅能够高效处理修改和查询操作,还具备较好的实际应用潜力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目描述 Description
人类喜欢许下各种各样的祈愿,祈愿的感应强度是可以用数值来表示的,感应强度较强的祈愿会首先被神灵所响应而得到实现.

但是因为人类的祈愿太多了,现在SatiyaAugust已经没有办法很快的选出她想要帮助人类实现的祈愿了.

现在有一些祈愿,每个祈愿有自己的数值,SatiyaAugust让这些祈愿排成了一列.她会向你询问在一段区间l~r中第k大的祈愿,但是人类是一种奇怪的生物,他们的祈愿大小会发生变化.

一句话题意:单点修改动态k区间第k小

输入描述 Input Description
第一行一个数T,表示数据组数.

每组数据第一行是两个数n,m,n表示祈愿数目,m表示修改和询问的总数.

接下来一行n个数,表示n个祈愿初始数值.

接下来m行

每行开头是一个字母

若为Q,则下面有三个整数l,r,k,表示查询a[l]~a[r]中第k小

若为C,则接下来两个整数x,k,表示修改a[x]为k

输出描述 Output Description
对每个询问,输出相应的值

样例输入 Sample Input
1

2 3

1 2

Q 1 2 1

C 1 3

Q 1 2 1

样例输出 Sample Output
1

2

数据范围及提示 Data Size & Hint
T<=5

N<=50000 M<=10000 0<=a[i]<=10^9

做CA爷的题目前当然应该先%%%CA爷辣!
首先看到多组询问不要害怕。。。因为时限有4s,可以当做1组询问1s来做(反正最多5组 哼~) 但是不要想水分。。。亲测全都是5w的。。。良心数据。。(别打喔)
那我们来分析一下这道题。。。
做法有很多。。首先讨论喔不会的那种。。。分块。
时间复杂度O(m*sqrt(n)*logn*logn)空间复杂度O(n)然后。。。没有然后了都说了我不会←_←
第二种:权值线段树套区间树 这个做法首先要注意的是一定要离散化权值,毕竟10^9。。还有就是不要省空间,256MB足够你干很多事了,拿空间换时间很实惠的。。。
第三种:归并树。顾名思义,利用归并排序的思想在线段树上实现。然而并不是排序!!!每一个节点记录两个序列:一个有序,一个无序。然后从底向上逐渐合并实现查询区间第K大。单点修改复杂度O(nlogn),总复杂度O(nlognlogn)美中不足的是写起来麻烦。。。(但是比第二种简单)
第四种:手写set。。这个应该需要@P党(绝不是语言歧视。。而是C艹党除了大神没有手写set的习惯。。)
(我也只是想出来了做法所以就贴个原题题解的程序了。。。。不要吐槽我)

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int MAXN(50010);

template<typename T>
bool checkmin(T &a, const T &b){
    return b < a? (a = b, true): false;
}
template<typename T>
bool checkmax(T &a, const T &b){
    return b > a? (a = b, true): false;
}

struct RAND{
    int rn[MAXN], si, n;
    void init(int n){
        this->n = n;
        srand(2357);
        for(int i = 0; i < n; ++i) rn[i] = rand();
        si = 0;
    }
    int query(){
        if(si >= n) si -= n;
        return rn[si++];
    }
} ra;

struct NODE1{
    int ke, b, si;
    NODE1 *ch[2];
} *NIL1;

struct POOL1{
    NODE1 pool[MAXN*19], *last;
    void init(){
        last = pool;
    }
    NODE1 *all(int k){
        last->ch[0] = last->ch[1] = NIL1;
        last->ke = k;
        last->b = ra.query();
        last->si = 1;
        return last++;
    }
} pool1;

inline void rep(NODE1 *rt){
    if(rt == NIL1) return;
    rt->si = rt->ch[0]->si+rt->ch[1]->si+1;
}

void rot(NODE1 *&rt, int f){
    NODE1 *s = rt->ch[f];
    rt->ch[f] = s->ch[!f];
    s->ch[!f] = rt;
    rep(rt);
    rep(s);
    rt = s;
}

void Insert(NODE1 *&rt, int k){
    if(rt == NIL1){
        rt = pool1.all(k);
        return;
    }
    int t = (k >= rt->ke);
    Insert(rt->ch[t], k);
    if(rt->ch[t]->b > rt->b) rot(rt, t);
    rep(rt);
}

void Erase(NODE1 *&rt, int k){
    if(rt->ke == k){
        if(rt->ch[0] == NIL1 || rt->ch[1] == NIL1){
            rt = rt->ch[rt->ch[0] == NIL1];
            return;
        }
        int t = rt->ch[0]->b < rt->ch[1]->b;
        rot(rt, t);
        Erase(rt->ch[!t], k);
        rep(rt);
        return;
    }
    Erase(rt->ch[k >= rt->ke], k);
    rep(rt);
}

int Lower_bound(NODE1 *rt, int k){
    if(rt == NIL1) return 0;
    if(k <= rt->ke) return Lower_bound(rt->ch[0], k);
    return rt->ch[0]->si+1+Lower_bound(rt->ch[1], k);
}

int arr[MAXN];
NODE1 *srt[MAXN << 2];

int query(int ql, int qr, int k, int l, int r, int rt){
    if(srt[rt] == 0){
        srt[rt] = NIL1;
        for(int i = l; i <= r; ++i) Insert(srt[rt], arr[i]);
    }
    if(ql <= l && qr >= r) return Lower_bound(srt[rt], k);
    int m = (l+r)/2, ret = 0;
    if(ql <= m) ret += query(ql, qr, k, l, m, rt << 1);
    if(qr > m) ret += query(ql, qr, k, m+1, r, (rt << 1)|1);
    return ret;
}

void update(int loc, int k, int l, int r, int rt){
    if(srt[rt] == 0){
        srt[rt] = NIL1;
        for(int i = l; i <= r; ++i) Insert(srt[rt], arr[i]);
    }
    if(l <= loc && loc <= r){
        Erase(srt[rt], arr[loc]);
        Insert(srt[rt], k);
    }
    if(l == r) return;
    int m = (l+r)/2;
    if(loc <= m) update(loc, k, l, m, rt << 1);
    else update(loc, k, m+1, r, (rt <<1)|1);
}

int main(){
    ra.init(50000);
    NIL1 = new NODE1();
    NIL1->ch[0] = NIL1->ch[1] = NIL1;
    NIL1->b = -1;
    NIL1->si = 0;
    int TC;
    scanf("%d", &TC);
    while(TC--){
        int n, M, a, b, c;
        char s[2];
        scanf("%d%d", &n, &M);
        pool1.init();
        memset(srt, 0, sizeof(srt));
        int mi = 1000000001, mx = -1;
        for(int i = 1; i <= n; ++i){
            scanf("%d", arr+i);
            checkmin(mi, arr[i]);
            checkmax(mx, arr[i]);
        }
        for(int i = 0; i < M; ++i){
            scanf("%s", s);
            if(s[0] == 'Q'){
                scanf("%d%d%d", &a, &b, &c);
                int l = mi, r = mx, m, t;
                while(l <= r){
                    m = (l+r)/2;
                    t = query(a, b, m, 1, n, 1);
                    if(t < c) l = m+1;
                    else r = m-1;
                }
                printf("%d\n", l-1);
            }else{
                scanf("%d%d", &a, &b);
                update(a, b, 1, n, 1);
                arr[a] = b;
                checkmin(mi, b);
                checkmax(mx, b);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值