洛谷P2617 Dynamic Rankings (主席樹)

洛谷P2617 Dynamic Rankings

題目描述

給定一個含有n個數的序列a[1],a[2],a[3]……a[n],程序必須回答這樣的詢問:對於給定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的數是多少(1≤k≤j-i+1),而且,你能夠改變一些a[i]的值,改變後,程序還能針對改變後的a繼續回答上面的問題。你須要編一個這樣的程序,從輸入文件中讀入序列a,而後讀入一系列的指令,包括詢問指令和修改指令。c++

對於每個詢問指令,你必須輸出正確的回答。git

輸入輸出格式

輸入格式:github

第一行有兩個正整數n(1≤n≤10000),m(1≤m≤10000)。分別表示序列的長度和指令的個數。數組

第二行有n個數,表示a[1],a[2]……a[n],這些數都小於10^9。接下來的m行描述每條指令,每行的格式是下面兩種格式中的一種。 Q i j k 或者 C i t優化

  • Q i j k (i,j,k是數字,1≤i≤j≤n, 1≤k≤j-i+1)表示詢問指令,詢問a[i],a[i+1]……a[j]中第k小的數。spa

  • C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改變成爲t。code

輸出格式:繼承

對於每一次詢問,你都須要輸出他的答案,每個輸出佔單獨的一行。get

輸入輸出樣例

輸入樣例#1:it

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

輸出樣例#1:

3
6

說明

10%的數據中,m,n≤100;

20%的數據中,m,n≤1000;

50%的數據中,m,n≤10000。

對於全部數據,m,n≤100000

請注意常數優化,但寫法正常的總體二分和樹套樹均可以以大約1000ms每一個點的時間過。

來源:bzoj1901

本題數據爲洛谷自造數據,使用CYaRon耗時5分鐘完成數據製做。

Solution

帶修改主席樹...

咱們都知道靜態主席樹每顆節點保存當前管理區間的全部權值的出現次數,由於每棵樹的形態結構都相同,因此咱們能夠經過對樹進行相加減求得靜態區間第k大(小)

可是若是要修改的話,若咱們暴力修改的話是須要對前綴和進行\(O(nlogn)\)的單次修改,會T飛

那麼咱們就須要請擅長管理前綴和的樹狀數組來處理,而主席樹如今只負責維護前綴和的輔助數組,套上樹狀數組的話,咱們就只須要修改\(logn\)的節點,複雜度\(O(log^2n)\)

注意1:與靜態主席樹不一樣的是帶修改主席樹中的每一棵線段樹所管理的信息都是獨立的,不須要繼承上一棵線段樹的信息

注意2:不但須要對原數組進行離散,還要把修改以後的值也加進來,否則修改後的值不必定能在離散數組中找獲得位置

Code

#include<bits/stdc++.h>
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define rg register
#define mid ((l+r)>>1)
#define in(i) (i=read())
using namespace std;

const int N=1e5+10;

int read() {
    int ans=0,f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
    return ans*=f;
}

int n,m,num,tot,cnt1,cnt2;
int a[N],h[N<<1],now[5][N<<8],rt[N<<8];

struct Tree {
    int v,l,r;
}t[N<<8];

struct Query{
    int op,l,r,k;
}q[N];

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

void update(int &u,int l,int r,int pos,int val) {//不須要繼承上一棵線段樹的信息,帶修改主席樹的每一棵線段樹管理的信息都是獨立的
    if(!u) u=++tot; t[u].v+=val;
    if(l==r) return;
    if(pos<=mid) update(t[u].l,l,mid,pos,val);
    else update(t[u].r,mid+1,r,pos,val);
}

void modify(int x,int val) {//處理出哪些節點須要修改
    int k=lower_bound(h+1,h+1+num,a[x])-h;
    for (int i=x;i<=n;i+=lowbit(i)) update(rt[i],1,num,k,val);
}

int query(int l,int r,int k,int sum=0) {
    if(l==r) return h[l];
    for (int i=1;i<=cnt1;i++) sum-=t[t[now[1][i]].l].v;
    for (int i=1;i<=cnt2;i++) sum+=t[t[now[2][i]].l].v;
    if(k<=sum) {
        for (int i=1;i<=cnt1;i++) now[1][i]=t[now[1][i]].l;//若是當前左子樹的前綴次數小於等於k,那麼答案就在左子樹中
        for (int i=1;i<=cnt2;i++) now[2][i]=t[now[2][i]].l;
        return query(l,mid,k);
    }
    else {//不然在右子樹中
        for (int i=1;i<=cnt1;i++) now[1][i]=t[now[1][i]].r;
        for (int i=1;i<=cnt2;i++) now[2][i]=t[now[2][i]].r;
        return query(mid+1,r,k-sum);
    }
}

int pre_query(int l,int r,int k) {//處理出哪些節點須要查詢
    cnt1=cnt2=0, l--;
    for (int i=l;i;i-=lowbit(i)) now[1][++cnt1]=rt[i];
    for (int i=r;i;i-=lowbit(i)) now[2][++cnt2]=rt[i];
    return query(1,num,k);
}

int main()
{
    in(n), in(m);
    for (int i=1;i<=n;i++) in(a[i]),h[++num]=a[i];
    for (int i=1;i<=m;i++) {
        char s[10]; scanf("%s",s);
        if(s[0]=='Q') in(q[i].l), in(q[i].r), in(q[i].k), q[i].op=1;
        else in(q[i].l), in(q[i].r), h[++num]=q[i].r, q[i].op=0;
    }
    sort(h+1,h+1+num), num=unique(h+1,h+1+num)-h-1;
    for (int i=1;i<=n;i++) modify(i,1);
    for (int i=1;i<=m;i++) {
        if(q[i].op==0) {
            modify(q[i].l,-1);
            a[q[i].l]=q[i].r;
            modify(q[i].l,1);
        }
        else printf("%d\n",pre_query(q[i].l,q[i].r,q[i].k));
    }
}
相關文章
相關標籤/搜索