洛谷P1533 可怜的狗狗

洛谷P1533 可怜的狗狗

平衡树 莫队

题目传送门

一看到平衡树的标签就上去杠了。。。结果发现是主席树裸题。。。然而我不会

求区间第k大。因为是离线,所以可以乱搞。这里我用的是平衡树+莫队。

对询问进行排序,然后用莫队的思想进行插入/删除节点,最后查询答案。这些操作可以用平衡树。

然后就艹过去了。。。

代码:

#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define N 300000
#define M 50000
#define inf -0x7fffffff
using namespace std;
struct node{
    int w,rnd,to[2],p,size;
}t[N*10+5];//这里极端情况应该要2700w的
struct qstn{
    int l,r,k,bl,br,id;
}que[M+5];
int n,m,rt,S,nd;
int ans[M+5],a[N+5];
inline char readc(){//fread读优
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline int _read(){
    int num=0,f=1; char ch=readc();
    while (!isdigit(ch)) {if (ch=='-') f=-f;ch=readc();}
    while (isdigit(ch)) num=num*10+ch-48,ch=readc();
    return num*f;
}
int f(int x){ return x/S; }//分块
bool cmp(qstn x,qstn y){ return (f(x.l)<f(y.l))||(f(x.l)==f(y.l)&&x.r<y.r); }
void rtt(int &x,int fl){
    int s=t[x].to[fl];
    t[x].to[fl]=t[s].to[fl^1];
    t[s].to[fl^1]=x;
    t[s].size=t[x].size;
    t[x].size=t[t[x].to[0]].size+t[t[x].to[1]].size+t[x].p;
    x=s;
}
void nsrt(int &x,int w){
    if (!x){
        t[x=++nd].w=w; t[x].p=t[x].size=1; 
        t[x].rnd=rand(); return;
    }
    t[x].size++;
    if (t[x].w==w) t[x].p++;
    else{
        int flag=t[x].w<w;
        nsrt(t[x].to[flag],w);
        if (t[x].rnd>t[t[x].to[flag]].rnd) rtt(x,flag);
    }
}
void dlt(int &x,int w){
    if (!x) return;
    if (t[x].w==w){
        if (t[x].p>1) { t[x].p--; t[x].size--; return; }
        if (!t[x].to[0]) { x=t[x].to[1]; return; } 
        if (!t[x].to[1]) { x=t[x].to[0]; return; }
        int flag=t[t[x].to[0]].rnd<t[t[x].to[1]].rnd;
        rtt(x,flag^1),dlt(x,w);
    }
    else{
        t[x].size--;
        int flag=t[x].w<w;
        dlt(t[x].to[flag],w);
    }
}
int srch(int x,int w){
    if (!x) return 0;
    if (t[t[x].to[0]].size>=w) 
        return srch(t[x].to[0],w);
    if (t[t[x].to[0]].size+t[x].p>=w)
        return t[x].w;
    return srch(t[x].to[1],w-t[t[x].to[0]].size-t[x].p);
}
void Insert(int l,int r){ for (int i=l;i<=r;i++) nsrt(rt,a[i]); }//插入(平衡树维护)
void Delete(int l,int r){ for (int i=l;i<=r;i++) dlt(rt,a[i]); }//删除(平衡树维护)
int main(){
    n=_read(),m=_read(),S=sqrt(n);
    for (int i=1;i<=n;i++) a[i]=_read();
    for (int i=1;i<=m;i++){
        que[i].l=_read(),que[i].r=_read();
        que[i].k=_read(),que[i].id=i;
    }
    sort(que+1,que+m+1,cmp);//排序
    Insert(que[1].l,que[1].r); //先把第一个询问加进去
    ans[que[1].id]=srch(rt,que[1].k);
    int l=que[1].l,r=que[1].r;
    for (int i=2;i<=m;i++){//莫队乱搞
        if (que[i].l>l) Delete(l,que[i].l-1);
        else Insert(que[i].l,l-1);
        if (que[i].r>r) Insert(r+1,que[i].r);
        else Delete(que[i].r+1,r);
        ans[que[i].id]=srch(rt,que[i].k);
        l=que[i].l,r=que[i].r;
    }
    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值