zoj2112 Dynamic Rankings (主席树 || 树套树)

本文介绍了一种处理区间内元素查询与修改的问题,通过使用主席树或树套树的方法,有效地解决了区间第K小值查询及单点更新的问题。

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

The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.

Your task is to write a program for this computer, which

- Reads N numbers from the input (1 <= N <= 50,000)

- Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], ..., a[j] and change some a[i] to t.


Input

The first line of the input is a single number X (0 < X <= 4), the number of the test cases of the input. Then X blocks each represent a single test case.

The first line of each block contains two integers N and M, representing N numbers and M instruction. It is followed by N lines. The (i+1)-th line represents the number a[i]. Then M lines that is in the following format

Q i j k or
C i t

It represents to query the k-th number of a[i], a[i+1], ..., a[j] and change some a[i] to t, respectively. It is guaranteed that at any time of the operation. Any number a[i] is a non-negative integer that is less than 1,000,000,000.

There're NO breakline between two continuous test cases.


Output

For each querying operation, output one integer to represent the result. (i.e. the k-th smallest number of a[i], a[i+1],..., a[j])

There're NO breakline between two continuous test cases.


Sample Input

2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3


Sample Output

3
6
3
6


题意:给你一个长为n的区间,有m个询问和修改,每次把下标i对应的值改变,或者询问某段区间的第k小值。

思路:这题是很经典的题,可以用主席树和树套树做,对于树套树的做法,我是用线段树的每一个节点都存一个treap,然后每一次修改就是单点更新,修改的时间复杂度为O(n*logn*logn),询问时,需要二分值val,并统计线段树中对应的区间小于val值的个数,询问的时间复杂度为O(n*logn*logn*logn),所以总的时间复杂度为O(n*logn*logn*logn),同时因为线段树最多有logn层,每层都是n个节点,所以treap的空间复杂度为O(n*logn).


#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<algorithm>
using namespace std;
#define lson th<<1
#define rson th<<1|1
typedef long long ll;
typedef long double ldb;
#define inf 99999999
#define pi acos(-1.0)
#define maxn 50005
int rt[maxn*4],a[maxn],n;
int cnt;
struct Treap{
    int key,pri,siz,num,son[2];
    void newnode(int x,int y){
        key=x;pri=y;
        siz=num=1;
        son[0]=son[1]=0;
    }
}T[20*maxn];


void rotate(int p,int &x)
{
    int y=T[x].son[!p];
    T[x].siz=T[x].siz-T[y].siz+T[T[y].son[p] ].siz;
    T[x].son[!p]=T[y].son[p];
    T[y].siz=T[y].siz-T[T[y].son[p] ].siz+T[x].siz;
    T[y].son[p]=x;
    x=y;
}

void charu(int key,int &x)
{
    if(x==0){
        x=++cnt;
        T[x].newnode(key,rand());
    }
    else{
        T[x].siz++;
        if(T[x].key==key){
            T[x].num++;return;
        }
        int p=key<T[x].key;
        charu(key,T[x].son[!p]);
        if(T[x].pri<T[T[x].son[!p] ].pri )
            rotate(p,x);
    }
}
void del(int key, int &x)
{
    if(T[x].key == key)
    {
        if(T[x].num>1){
            T[x].siz--;
            T[x].num--;
            return;
        }
        if(T[x].son[0] && T[x].son[1])
        {
            int p=T[T[x].son[0]].pri>T[T[x].son[1]].pri;
            rotate(p,x);
            del(key,x);
        }
        else x=T[x].son[1]+T[x].son[0];
    }
    else
    {
        T[x].siz--;
        int p=T[x].key>key;
        del(key,T[x].son[!p]);
    }
}
int query_rank(int key,int &x)
{
    if(x==0)return 0;
    if(T[x].key==key)return T[T[x].son[0] ].siz;
    if(T[x].key>key)return query_rank(key,T[x].son[0]);
    if(T[x].key<key)return T[T[x].son[0] ].siz+T[x].num+query_rank(key,T[x].son[1]);
}



void update(int idx,int val,int L,int R,int th,int f)
{
    if(f)del(a[idx],rt[th]);
    charu(val,rt[th]);
    if(L==idx && R==idx){
        a[idx]=val;
        return;
    }
    int mid=(L+R)/2;
    if(idx<=mid)update(idx,val,L,mid,lson,f);
    else update(idx,val,mid+1,R,rson,f);
}


int question(int l,int r,int val,int L,int R,int th)
{
    int mid;
    if(l==L && r==R){
        return query_rank(val,rt[th]);
    }
    mid=(L+R)/2;
    if(r<=mid)return question(l,r,val,L,mid,lson);
    else if(l>mid)return question(l,r,val,mid+1,R,rson);
    else return question(l,mid,val,L,mid,lson)+question(mid+1,r,val,mid+1,R,rson);
}

int main()
{
    int m,i,j,Tcase,l,r,c,d,e,mid;
    char s[10];
    scanf("%d",&Tcase);
    while(Tcase--)
    {
        memset(rt,0,sizeof(rt));
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        cnt=0;
        for(i=1;i<=n;i++){
            update(i,a[i],1,n,1,0);
        }
        for(i=1;i<=m;i++){
            scanf("%s",s);
            if(s[0]=='C'){
                scanf("%d%d",&c,&d);
                update(c,d,1,n,1,1);
            }
            else{
                scanf("%d%d%d",&c,&d,&e);
                l=0;r=1000000000;
                while(l<=r){
                    mid=(l+r)/2;
                    //printf("%d %d %d\n",l,r,mid);
                    if(question(c,d,mid,1,n,1)>=e)r=mid-1;
                    else l=mid+1;
                }
                printf("%d\n",r);
            }
        }

    }
    return 0;
}

/*
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
*/

主席树做法:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef long double ldb;
#define lth th<<1
#define rth th<<1|1
#define inf 99999999
#define pi acos(-1.0)
#define maxn 60005
#define M 2500010
int a[maxn],n;
struct node{
    int l,r,kind,d;
}ques[10005];
int pos[maxn],T[maxn],S[maxn];
int lson[M],rson[M],c[M];
int th,tot;

int build(int l,int r)
{
    int newroot=++th,i,j,mid;
    c[newroot]=0;   //!!!
    if(l!=r){
        mid=(l+r)/2;
        lson[newroot]=build(l,mid);
        rson[newroot]=build(mid+1,r);
    }
    return newroot;
}

int update(int root,int zhi,int value)
{
    int i,j;
    int newroot=++th;int tmp=newroot;
    int l=1,r=tot,mid;
    c[newroot]=c[root]+value;
    while(l<r){
        mid=(l+r)/2;
        if(zhi<=mid){
            r=mid;
            lson[newroot ]=++th;rson[newroot]=rson[root];
            newroot=lson[newroot];root=lson[root];
        }
        else{
            l=mid+1;
            lson[newroot ]=lson[root];rson[newroot]=++th;
            newroot=rson[newroot];root=rson[root];
        }
        c[newroot]=c[root]+value;
    }
    return tmp;
}

int lowbit(int x)
{
    return x&(-x);
}

void modify(int pos,int zhi,int value)
{
    int i,j;
    while(pos<=n){
        S[pos]=update(S[pos],zhi,value);
        pos+=lowbit(pos);
    }
}
int use[maxn];
int getsum(int pos)
{
    int sum=0;
    while(pos>0){
        sum+=c[lson[use[pos] ] ];
        pos-=lowbit(pos);
    }
    return sum;

}
int question(int left,int right,int k)
{
    int i,j,root1,root2;
    int l=1,r=tot,mid;
    for(i=left-1;i>0;i-=lowbit(i))use[i]=S[i];
    for(i=right;i>0;i-=lowbit(i))use[i]=S[i];
    root1=T[left-1];root2=T[right];

    while(l<r){
        mid=(l+r)/2;
        int tmp=getsum(right)-getsum(left-1)+c[lson[root2] ]-c[lson[root1] ];
        if(tmp>=k){
            r=mid;
            root1=lson[root1];root2=lson[root2];
            for(i=left-1;i>0;i-=lowbit(i))use[i]=lson[use[i] ];
            for(i=right;i>0;i-=lowbit(i))use[i]=lson[use[i] ];
        }
        else{
            k-=tmp;
            l=mid+1;
            root1=rson[root1];root2=rson[root2];
            for(i=left-1;i>0;i-=lowbit(i))use[i]=rson[use[i] ];
            for(i=right;i>0;i-=lowbit(i))use[i]=rson[use[i] ];
        }
    }
    return l;
}

int main()
{
    int m,i,j,Tcase,c,d,e;
    char str[5];
    scanf("%d",&Tcase);
    while(Tcase--)
    {
        scanf("%d%d",&n,&m);
        tot=0;
        for(i=1;i<=n;i++){
            scanf("%d",&a[i]);
            tot++;
            pos[tot]=a[i];
        }
        for(i=1;i<=m;i++){
            scanf("%s",str);
            if(str[0]=='Q'){
                scanf("%d%d%d",&c,&d,&e);
                ques[i].kind=0;ques[i].l=c;ques[i].r=d;ques[i].d=e;
            }
            else if(str[0]=='C'){
                scanf("%d%d",&c,&d);
                tot++;pos[tot]=d;
                ques[i].kind=1;ques[i].l=c;ques[i].r=d;
            }
        }

        sort(pos+1,pos+1+tot);
        tot=unique(pos+1,pos+1+tot)-pos-1;

        th=0;
        T[0]=build(1,tot);


        for(i=1;i<=n;i++){
            int t=lower_bound(pos+1,pos+1+tot,a[i])-pos;
            T[i]=update(T[i-1],t,1);
        }

        for(i=1;i<=n;i++){
            S[i]=T[0];
        }
        for(i=1;i<=m;i++){
            if(ques[i].kind==0){   //表示询问
                printf("%d\n",pos[question(ques[i].l,ques[i].r,ques[i].d)]);
            }
            else{
                int t1=lower_bound(pos+1,pos+1+tot,a[ques[i].l])-pos;
                int t2=lower_bound(pos+1,pos+1+tot,ques[i].r)-pos;
                modify(ques[i].l,t1,-1);
                modify(ques[i].l,t2,1);
                a[ques[i].l ]=ques[i].r;  //!!!
            }
        }

    }
    return 0;
}

/*
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
*/



### ZOJ 1088 线段 解题思路 #### 题目概述 ZOJ 1088 是一道涉及动态维护区间的经典问题。通常情况下,这类问题可以通过线段来高效解决。题目可能涉及到对数组的区间修改以及单点查询或者区间查询。 --- #### 线段的核心概念 线段是一种基于分治思想的数据结构,能够快速处理区间上的各种操作,比如求和、最大值/最小值等。其基本原理如下: - **构建阶段**:通过递归方式将原数组划分为多个小区间,并存储在二叉形式的节点中。 - **更新阶段**:当某一段区间被修改时,仅需沿着对应路径向下更新部分节点即可完成全局调整。 - **查询阶段**:利用懒惰标记(Lazy Propagation),可以在 $O(\log n)$ 时间复杂度内完成任意范围内的计算。 具体到本题,假设我们需要支持以下两种主要功能: 1. 对指定区间 `[L, R]` 执行某种操作(如增加固定数值 `val`); 2. 查询某一位置或特定区间的属性(如总和或其他统计量)。 以下是针对此场景设计的一种通用实现方案: --- #### 实现代码 (Python) ```python class SegmentTree: def __init__(self, size): self.size = size self.tree_sum = [0] * (4 * size) # 存储区间和 self.lazy_add = [0] * (4 * size) # 延迟更新标志 def push_up(self, node): """ 更新父节点 """ self.tree_sum[node] = self.tree_sum[2*node+1] + self.tree_sum[2*node+2] def build_tree(self, node, start, end, array): """ 构建线段 """ if start == end: # 到达叶节点 self.tree_sum[node] = array[start] return mid = (start + end) // 2 self.build_tree(2*node+1, start, mid, array) self.build_tree(2*node+2, mid+1, end, array) self.push_up(node) def update_range(self, node, start, end, l, r, val): """ 区间更新 [l,r], 加上 val """ if l <= start and end <= r: # 当前区间完全覆盖目标区间 self.tree_sum[node] += (end - start + 1) * val self.lazy_add[node] += val return mid = (start + end) // 2 if self.lazy_add[node]: # 下传延迟标记 self.lazy_add[2*node+1] += self.lazy_add[node] self.lazy_add[2*node+2] += self.lazy_add[node] self.tree_sum[2*node+1] += (mid - start + 1) * self.lazy_add[node] self.tree_sum[2*node+2] += (end - mid) * self.lazy_add[node] self.lazy_add[node] = 0 if l <= mid: self.update_range(2*node+1, start, mid, l, r, val) if r > mid: self.update_range(2*node+2, mid+1, end, l, r, val) self.push_up(node) def query_sum(self, node, start, end, l, r): """ 查询区间[l,r]的和 """ if l <= start and end <= r: # 完全匹配 return self.tree_sum[node] mid = (start + end) // 2 res = 0 if self.lazy_add[node]: self.lazy_add[2*node+1] += self.lazy_add[node] self.lazy_add[2*node+2] += self.lazy_add[node] self.tree_sum[2*node+1] += (mid - start + 1) * self.lazy_add[node] self.tree_sum[2*node+2] += (end - mid) * self.lazy_add[node] self.lazy_add[node] = 0 if l <= mid: res += self.query_sum(2*node+1, start, mid, l, r) if r > mid: res += self.query_sum(2*node+2, mid+1, end, l, r) return res def solve(): import sys input = sys.stdin.read data = input().split() N, Q = int(data[0]), int(data[1]) # 数组大小 和 操作数量 A = list(map(int, data[2:N+2])) # 初始化数组 st = SegmentTree(N) st.build_tree(0, 0, N-1, A) idx = N + 2 results = [] for _ in range(Q): op_type = data[idx]; idx += 1 L, R = map(int, data[idx:idx+2]); idx += 2 if op_type == 'Q': # 查询[L,R]的和 result = st.query_sum(0, 0, N-1, L-1, R-1) results.append(result) elif op_type == 'U': # 修改[L,R]+X X = int(data[idx]); idx += 1 st.update_range(0, 0, N-1, L-1, R-1, X) print("\n".join(map(str, results))) solve() ``` --- #### 关键点解析 1. **初始化与构建**:在线段创建过程中,需要遍历输入数据并将其映射至对应的叶子节点[^1]。 2. **延迟传播机制**:为了优化性能,在执行批量更新时不立即作用于所有受影响区域,而是记录更改意图并通过后续访问逐步生效[^2]。 3. **时间复杂度分析**:由于每层最多只访问两个子分支,因此无论是更新还是查询都维持在 $O(\log n)$ 范围内[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值