P2617 Dynamic Rankings(动态区间第K小,单点更新) 树状数组套主席树(权值线段树)

题目
Satisfied
题解:与主席树前缀和的思想一致,树状数组维护一个"主席树"(不是前缀和那样的前i颗权值线段树的和 而是low(i)颗权值线段树的和 )平常一个主席树root[i]是[1,i]的和。现在是[i-low(i)+1,i]的和。
在这里插入图片描述
输入样例#1:
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

输出样例#1:
3
6

说明

对于所有数据,m,n≤100000
请注意常数优化,但写法正常的整体二分和树套树都可以大约1000ms每个点的时间过。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define low(a) a&-a
#define m(a,b) memset(a,b,sizof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=2e5+5,M=N;
int a[N],refl[N],tot,n,m;
int sum[N*500],root[N],bit1[N],bit2[N],sz;
struct Query{int opt,l,r,k;}q[M];
struct tree{int l,r;}t[N*500];
void update(int &x,int wh,int l,int r,int val)
{
    if(!x)
        x=++sz;
    sum[x]+=val;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    if(wh<=mid)
        update(t[x].l,wh,l,mid,val);
    else
        update(t[x].r,wh,mid+1,r,val);
}
void modify(int pos,int val)
{
    int dex=lower_bound(refl+1,refl+tot+1,a[pos])-refl;
    int i=pos;
    while(i<=n)
    {
        update(root[i],dex,1,tot,val);
        i+=low(i);
    }
}
int query(int o1,int o2,int l,int r,int k)
{
    if(l==r)
        return l;
    int left=0;
    for(int i=1;i<=o1;i++)
        left-=sum[t[bit1[i]].l];
    for(int i=1;i<=o2;i++)
        left+=sum[t[bit2[i]].l];
    int mid=(l+r)>>1;
    if(left>=k)
    {
        for(int i=1;i<=o1;i++)
            bit1[i]=t[bit1[i]].l;
        for(int i=1;i<=o2;i++)
            bit2[i]=t[bit2[i]].l;
        return query(o1,o2,l,mid,k);
    }
    else
    {
        for(int i=1;i<=o1;i++)
            bit1[i]=t[bit1[i]].r;
        for(int i=1;i<=o2;i++)
            bit2[i]=t[bit2[i]].r;
        return query(o1,o2,mid+1,r,k-left);
    }
}
int getans(int l,int r,int k)
{
    int o1=0,o2=0;
    for(int i=l-1;i;i-=low(i))
        bit1[++o1]=root[i];
    for(int i=r;i;i-=low(i))
        bit2[++o2]=root[i];
    return query(o1,o2,1,tot,k);
}
int main()
{
    sz=0;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),refl[i]=a[i];
    tot=n;char s[3];
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        if(s[0]=='Q')
            scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k),q[i].opt=1;
        else
            scanf("%d%d",&q[i].l,&q[i].k),q[i].opt=2,refl[++tot]=q[i].k;
    }
    sort(refl+1,refl+tot+1);
    tot=unique(refl+1,refl+tot+1)-(refl+1);
    for(int i=1;i<=n;i++)
        modify(i,1);
    for(int i=1;i<=m;i++)
    {
        if(q[i].opt==1)
            printf("%d\n",refl[getans(q[i].l,q[i].r,q[i].k)]);
        else
        {
            modify(q[i].l,-1);
            a[q[i].l]=q[i].k;
            modify(q[i].l,1);
        }
    }
}

还可以 带修莫队+对权值分块(因为更改好多次,查询只有m次) :待补(2天之内)
还可以 二分答案ans+分块求询问区间内<ans的个数 小于k的最大值 : 待补(2天之内)
还可以 整体二分:不会 (待补 随缘)
权值线段树(外)+区间线段树(内) 妥妥的TLE。(T_T)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define foru(i,a,b) for(int i=a;i<=b;++i)
#define m(a,b) memset(a,b,sizof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=2e5+5,M=1e5+5;
template<class T>void rd(T &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9')  {f|=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return;
}
int a[N>>1],refl[N],tot,n,m;
struct Query{int l,r,c;}q[N];
struct tree{int l,r;}t[N*500];
int root[N*40],sum[N*500],sz;
void in_span(int &x,int d,int val,int l,int r)
{
    if(!x) x=++sz;
    if(l==r)
    {
        sum[x]+=val;
        return;
    }
    int mid=(l+r)>>1;
    if(d<=mid) in_span(t[x].l,d,val,l,mid);
    else in_span(t[x].r,d,val,mid+1,r);
    sum[x]=sum[t[x].l]+sum[t[x].r];
}
void out_point(int wh,int val,int d,int l,int r,int pos)
{
    in_span(root[pos],d,val,1,n);
    if(l==r)
        return;
    int mid=(l+r)>>1;
    if(wh<=mid)
        out_point(wh,val,d,l,mid,pos<<1);
    else
        out_point(wh,val,d,mid+1,r,pos<<1|1);
}

int in_query(int x,int cl,int cr,int l,int r)
{
    if(cl<=l&r<=cr) return sum[x];
    int mid=(l+r)>>1,ans=0;
    if(cl<=mid) ans+=in_query(t[x].l,cl,cr,l,mid);
    if(cr>mid) ans+=in_query(t[x].r,cl,cr,mid+1,r);
    return ans;
}
int out_query(int nl,int nr,int l,int r,int pos,int k)
{
    if(l==r)
        return l;
    int mid=(l+r)>>1,leftsum=0;
    if(root[pos<<1])
        leftsum=in_query(root[pos<<1],nl,nr,1,n);
    if(leftsum>=k)
        return out_query(nl,nr,l,mid,pos<<1,k);
    else
        return out_query(nl,nr,mid+1,r,pos<<1|1,k-leftsum);
}
int main()
{
    rd(n),rd(m);
    foru(i,1,n) rd(a[i]),refl[i]=a[i];
    sort(refl+1,refl+n+1);
    tot=n;
    foru(i,1,m)
    {
        char ch[3];scanf("%s",ch);
        if(ch[0]=='Q')
            rd(q[i].l),rd(q[i].r),rd(q[i].c);
        else
            rd(q[i].l),rd(q[i].c),refl[++tot]=q[i].c;
    }
    sort(refl+1,refl+tot+1);
    tot=unique(refl+1,refl+tot+1)-(refl+1);
    foru(i,1,n)
    {
        a[i]=lower_bound(refl+1,refl+tot+1,a[i])-refl;
        out_point(a[i],1,i,1,tot,1);
    }
    foru(i,1,m)
    {
        if(!q[i].r)
        {
            out_point(a[q[i].l],-1,q[i].l,1,tot,1);
            a[q[i].l]=lower_bound(refl+1,refl+tot+1,q[i].c)-refl;
            out_point(a[q[i].l],1,q[i].l,1,tot,1);
        }
        else printf("%d\n",refl[out_query(q[i].l,q[i].r,1,tot,1,q[i].c)]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值