P2617 Dynamic Rankings

洛谷传送门

思路:

1.用树套树实现log修改。

2.查询的时候,把我们需要的(或者说容纳了询问区间所有值的线段树)--即构成这颗,查询区间权值总和线段树,的根存起来,求第k小的时候再来计算这颗权值总和线段树的左子树的和,然后判断是否进入这颗权值总和线段树的左子树或者右子树。

ps:这里还有个离散化,离线处理所有操作,把涉及到的权值全部放进数组里,然后用二分返回离散化后的值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
struct xdss
{
    int l,r,cnt;
}xds[N*32*6];
struct opss1
{
    char c;
    int l,r,k;
    int id,y;
}ops[N];
int root[N*32*6],pretree[N*32*6],curtree[N*32*6],val[N*2],a[N];
int n,m,cnt,idx,cnt2,cnt3;
int findid(int x)
{
     return lower_bound(val+1,val+1+cnt,x)-val;
}
void motify(int &rt,int l,int r,int pos,int vall)
{
    if(!rt) rt=++idx;
    xds[rt].cnt+=vall;
    if(l==r) return;
    int mid=l+r>>1;
    if(pos<=mid) motify(xds[rt].l,l,mid,pos,vall);
    else motify(xds[rt].r,mid+1,r,pos,vall);
}
int query(int l,int r,int k)
{
    if(l==r) return val[l];
    int zsum=0;
    for(int i=1;i<=cnt3;i++) zsum+=xds[xds[curtree[i]].l].cnt;
    for(int i=1;i<=cnt2;i++) zsum-=xds[xds[pretree[i]].l].cnt;
    int mid=l+r>>1;
    if(k<=zsum)
    {
        for(int i=1;i<=cnt3;i++) curtree[i]=xds[curtree[i]].l;
        for(int i=1;i<=cnt2;i++) pretree[i]=xds[pretree[i]].l;
        return query(l,mid,k);
    }
    else
   {
        k-=zsum;
        for(int i=1;i<=cnt3;i++) curtree[i]=xds[curtree[i]].r;
        for(int i=1;i<=cnt2;i++) pretree[i]=xds[pretree[i]].r;
        return query(mid+1,r,k);
    }
}
int lowbit(int x){return x&-x;}
void szmotify(int id,int pos,int vall){for(int i=id;i<=n;i+=lowbit(i)) motify(root[i],1,cnt,pos,vall);}
int szquery(int pre,int cur,int k)
{
     cnt2=cnt3=0;
    for(int i=pre;i;i-=lowbit(i)) pretree[++cnt2]=root[i];
    for(int i=cur;i;i-=lowbit(i)) curtree[++cnt3]=root[i];
   // cout<<cnt2<<' '<<cnt3<<'\n';
    return query(1,cnt,k);
}
void solve()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i],val[++cnt]=a[i];
    for(int i=1;i<=m;i++)
    {
        char op;
        cin>>op;
        if(op=='Q')
        {
            int l,r,k;
            cin>>l>>r>>k;
            ops[i]={op,l,r,k,0,0};
        }
        else
        {
            int id,y;
            cin>>id>>y;
            val[++cnt]=y;
            ops[i].c=op,ops[i].id=id,ops[i].y=y;
        }
    }
    sort(val+1,val+1+cnt);
    cnt=unique(val+1,val+1+cnt)-val-1;
    for(int i=1;i<=n;i++) szmotify(i,findid(a[i]),1);
    for(int i=1;i<=m;i++)
    {
        char c=ops[i].c;
        if(c=='Q')
        {
            int l=ops[i].l,r=ops[i].r,k=ops[i].k;
            cout<<szquery(l-1,r,k)<<'\n';
        }
        else
        {
            int id=ops[i].id,y=ops[i].y;
            szmotify(id,findid(a[id]),-1);
            swap(a[id],y);
            szmotify(id,findid(a[id]),1);
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T=1;
    while(T--){solve();}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值