Tyvj p3070 动态排名 (动态区间第K大)

本文介绍了Tyvj p3070题目的动态排名问题,需要维护一个序列并支持查询序列中升序排列的第k大数及修改序列元素的操作。解决方案采用线段树套平衡树和二分查找,实现复杂度为O(n*logn*logn*logn)。

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

题目链接

动态排名系统 20
[问题描述]
给定一个长度为 N 的已知序列 A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:
1、查询 A[i],A[i+1],A[i+2],...,A[j](1<=i<=j<=N)中,升序排列后排名第 k 的数。
2、修改 A[i]的值为 j。
所谓排名第 k,指一些数按照升序排列后,第 k 位的数。例如序列{6,1,9,6,6},排名第 3 的数
是 6,排名第 5 的数是 9。
[输入格式]
第一行包含一个整数 D(0<=D<=4),表示测试数据的数目。接下来有 D 组测试数据,每组测
试数据中,首先是两个整数 N(1<=N<=50000),M(1<=M<=10000),表示序列的长度为 N,有 M
个操作。接下来的 N 个不大于 1,000,000,000 正整数,第 i 个表示序列 A[i]的初始值。然后的
M 行,每行为一个操作

Q i j k 或者
C i j
分别表示查询 A[i],A[i+1],A[i+2],...,A[j](1<=i<=j<=N)中,升序排列后排名第 k 的数,和修改 A[i]
的值为 j。
[输出格式]
对于每个查询,输出一行整数,为查询的结果。测试数据之间不应有空行。


题解:方法很多,可用可持久化数据结构来做。

暂时只会线段树套平衡树+二分的方法,复杂度O(n*lgn*lgn*lgn)。

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
#include<string.h>
#include<vector>
#define nn 50010
#define mod 100003
typedef long long LL;
typedef unsigned long long LLU;
using namespace std;
struct node
{
    int val;
    int key;
    int num;
    int treenum;
    node* son[2];
};
void update(node *id)
{
    if(id==NULL)
        return ;
    id->treenum=id->num;
    for(int i=0;i<=1;i++)
    {
        if(id->son[i]!=NULL)
        {
            id->treenum+=id->son[i]->treenum;
        }
    }
}
void Rotate(node* &id,int d)
{
    node* tem=id->son[d];
    id->son[d]=tem->son[d^1];
    tem->son[d^1]=id;
    update(id);
    update(tem);
    id=tem;
}
void Insert(node* &id,int val)
{
    if(id==NULL)
    {
        id=new node;
        id->val=val;
        id->key=rand()*rand();
        id->num=1;
        id->treenum=1;
        id->son[0]=id->son[1]=NULL;
        return ;
    }
    if(id->val==val)
    {
        id->num++;
        update(id);
    }
    else if(val<id->val)
    {
        Insert(id->son[0],val);
        update(id);
        if(id->son[0]->key<id->key)
        {
            Rotate(id,0);
        }
    }
    else
    {
        Insert(id->son[1],val);
        update(id);
        if(id->son[1]->key<id->key)
        {
            Rotate(id,1);
        }
    }
}
void Delete(node* &id,int val)
{
    if(id==NULL)
        return ;
    if(val==id->val)
    {
        if(id->num==1)
        {
            if(id->son[0]==NULL)
            {
                node *k=id;
                id=id->son[1];
                delete k;
            }
            else if(id->son[1]==NULL)
            {
                node *k=id;
                id=id->son[0];
                delete k;
            }
            else if(id->son[0]->key<id->son[1]->key)
            {
                Rotate(id,0);
                Delete(id->son[1],val);
            }
            else
            {
                Rotate(id,1);
                Delete(id->son[0],val);
            }
        }
        else
        {
            id->num--;
        }
    }
    else if(val<id->val)
        Delete(id->son[0],val);
    else
        Delete(id->son[1],val);
    update(id);
}
int ask(node* id,int val,pair<int,int>&tem)
{
    if(id==NULL)
        return 0;
    if(id->val>=val)
    {
        if(id->val<tem.first)
        {
            tem=make_pair(id->val,id->num);
        }
        else if(id->val==tem.first)
        {
            tem.second+=id->num;
        }
        return ask(id->son[0],val,tem);//???
    }
    else
    {
        int re=id->num;
        if(id->son[0]!=NULL)
            re+=id->son[0]->treenum;
        return re+ask(id->son[1],val,tem);
    }
}
void Clear(node* id)
{
    if(id==NULL)
        return ;
    Clear(id->son[0]);
    Clear(id->son[1]);
    delete id;
}

node* Stree[132000];
int n,m,a[nn];
void build(int id,int l,int r)
{
    Stree[id]=NULL;
    for(int i=l;i<=r;i++)
        Insert(Stree[id],a[i]);
    if(l==r)
        return ;
    int mid=(l+r)/2;
    build(2*id,l,mid);
    build(2*id+1,mid+1,r);
}
void Change(int id,int l,int r,int wei,int val)
{
    Delete(Stree[id],a[wei]);
    Insert(Stree[id],val);
    if(l==r)
    {
        return ;
    }
    int mid=(l+r)/2;
    if(wei<=mid)
        Change(2*id,l,mid,wei,val);
    else
        Change(2*id+1,mid+1,r,wei,val);
}
int ve[nn],lv;
void Check(int id,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
    {
        ve[lv++]=id;
        return ;
    }
    int mid=(l+r)/2;
    if(L<=mid)
        Check(2*id,l,mid,L,R);
    if(R>mid)
        Check(2*id+1,mid+1,r,L,R);
}
int solve(int l,int r,int k)
{
    int i=0;
    int j=1000000000;
    int mid;
    pair<int,int>tem;
    int ix,e;
    lv=0;
    Check(1,1,n,l,r);
    while(i<=j)
    {
        tem=make_pair(1000000001,-1);
        mid=(i+j)/2;
        ix=0;
        for(e=0;e<lv;e++)
        {
            ix+=ask(Stree[ve[e]],mid,tem);
        }
        if(tem.second==-1)
        {
            j=mid-1;
            continue;
        }
        if(ix+1<=k&&k<=ix+tem.second)
        {
            return tem.first;
        }
        if(k<ix+1)
        {
            j=mid-1;
        }
        else
            i=mid+1;
    }
    return -1;
}
void Clear2(int id,int l,int r)
{
    Clear(Stree[id]);
    if(l==r)
        return ;
    int mid=(l+r)/2;
    Clear2(2*id,l,mid);
    Clear2(2*id+1,mid+1,r);
}
int main()
{
    int t,i;
    char s[5];
    int l,r,k;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        build(1,1,n);
        while(m--)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            {
                scanf("%d%d%d",&l,&r,&k);
                printf("%d\n",solve(l,r,k));
            }
            else
            {
                scanf("%d%d",&l,&r);
                Change(1,1,n,l,r);
                a[l]=r;
            }
        }
        Clear2(1,1,n);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值