Study Notes: Chairman of the tree (be persistent segment tree) (to be cushions)

But for this to learn what I did not learn the weights segment tree yet.

Chairman of the tree

Very big on?
In fact, be persistent data structure
In learning the weights tree line, we might think, you ask if any section of the \ (k \) small (large) we supposed to do?
Topic link: P3834 [template] can persist tree line 1 (Chairman of the tree)
is him!
At first thought and persistence can have nothing, but you listen to me.
We consider a maintenance \ ([0, l-1 ] \) weight range of value segment tree \ (T_1 \) , and maintenance \ ([0, r] \ ) weight range segment tree \ (T_2 \) then defined:
\ [T_l-T_2 \]
is the difference between the weight of each point, the new weights obtained segment tree \ (T \) , it maintains the \ ([l, r] \ ) weights, by problem can be solved on the run.
This time complexity is \ (O (Mn \ the n-log) \) , even more frightening is the space equivalent to \ (2m \) trees tree line, or forget it.
\ (\> \) \ (PS: \) is not from \ (0 \) interval that starts more awkward?
\ (\> \) \ (PS: \) due to respond to inquiries from the outset, in order to avoid the special judge, the weights are all built a \ (0 \) tree.
So we think of pretreatment each \ ([0, r] \ ) of the tree line, but still too much, then I steal a few pictures of it:
to throw a number of columns in a number of columns \ (4,1,1,2,8 , 9,4,4,3 \) , duplicates removed to obtain:
\ (1,2,3,4,8,9 \) .
\ ([0,9] \) Weight segment tree:

\ ([0,8] \) Weight segment tree:

\ ([0,7] \) Weight segment tree:

another hand to play with (I was only learned here, following all their own opinion of), we found that only one strand of each variable has changed!
So we construct such a Yike Duo root of the tree (it is important to update how many times there are that many root):

Time and space are reduced to excellent \ (O (the n-\ the n-log) \) (Bushi)
Here is the code :

Chairman constant large tree:

\ (Part \; 1 \) . Discretization:

Here the weight of the pot tree line, I almost autistic ......
Note \ (c \) is set to \ (1 \) , the blog post has been changed, the data will not be \ (WA \) .

struct Node
{
    int id,val;     
}t[MAXN];
int b[MAXN],num[MAXN],cntt[MAXN],c=1;
bool cmp(Node n,Node m){return n.val<m.val;} 
void hash(int n)
{
    sort(t+1,t+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        if(num[c]!=t[i].val) b[t[i].id]=++c,num[c]=t[i].val;
        else b[t[i].id]=c;
    }
}

Do not pay attention to statistics \ (CNTT \) , because of the following from the \ ([0,0] \) begin achievements, to continue to count the number of occurrences.

\ (Part \; 2 \) . Skeleton tree

这名是我自己给他起的\(qwq\)
就是权值都为\(0\)的树。
开始我这样理解主席树,就是将一条链拽下来,然后他所连的边也被拽下来了\(qwq\)
注意主席树点号较复杂,不能用\(×2\)的方法得到了,要记录下来。
这样写:

//骨架树qwq
struct node
{
    int l,r,ls,rs,sum;
    node()
    {
        l=r=ls=rs=sum=0;    
    }   
}a[MAXN<<5];
int cnt=0,root[MAXN];
void update(int k){a[k].sum=a[a[k].ls].sum+a[a[k].rs].sum;}
int build_bone(int l,int r)
{
    cnt++;//点的编号
    int op=cnt;//由于cnt是动态变化的,我们要把他存起来
    a[op].l=l,a[op].r=r;//表示的左右端点
    int mid=(l+r)>>1;
    if(l==r)
    {
        a[op].sum=0;
        return op;//根的左右儿子都是0 
    }
    a[op].ls=build_bone(l,mid),a[op].rs=build_bone(mid+1,r);
    update(op);
    return op;
}

\(Part\;3\).可持久化

俩个版本一起跑即可,注意判断变的点在左还是在右。

int build_chain(int k,int cur,int x)//对k点可持久化成x 
{
    cnt++;//点的编号
    int op=cnt;//由于cnt是动态变化的,我们要把他存起来
    a[op].l=a[cur].l,a[op].r=a[cur].r;//端点
    int mid=(a[cur].l+a[cur].r)>>1;
    if(a[cur].l==a[cur].r)
    {
        a[op].sum=x;
        return op;
    }
    //目标点是左儿子的,那么他和上一版本的左儿子依然不同,右儿子一样
    if(k<=mid) a[op].ls=build_chain(k,a[cur].ls,x),a[op].rs=a[cur].rs;
    ////目标点是优儿子的,那么他和上一版本的右儿子依然不同,左儿子一样
    else if(k>mid) a[op].rs=build_chain(k,a[cur].rs,x),a[op].ls=a[cur].ls;
    update(op);//记得更新
    return op;
}

\(Part\;4\).回答询问

每次做差即可,是\(O(1)\)的,剩下的等同于权值线段树。

//查询第x小值 
int query(int k1,int k2,int x)
{
    if(a[k1].l==a[k1].r) return num[a[k1].l];
    int mid=a[a[k1].ls].sum-a[a[k2].ls].sum;
    if(x<=mid) return query(a[k1].ls,a[k2].ls,x);
    else if(x>mid) return query(a[k1].rs,a[k2].rs,x-mid);
}

\(Part\;5\).处理根

只需枚举右端点,处理出每个根,询问时\(O(1)\)查询根,然后向下跑就行了。
大概是这样的:

root[0]=build_bone(1,c); //零号根即骨架树的根(是1)。
    for(int i=1;i<=n;i++) cntt[b[i]]++,root[i]=build_chain(b[i],root[i-1],cntt[b[i]]);
    for(int i=1;i<=m;i++)
    {
        l=read(),r=read(),k=read();
        printf("%d\n",query(root[r],root[l-1],k));  
    } 

总的说,时间复杂度为\(O((n+m)\log n)\),时间复杂度是\(O(n\log n)\),可以通过本题(然而他还是大常数主席树)。
下面放下\(AC\)代码:

\(Code\):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=2e5+5;
struct Node
{
    int id,val;     
}t[MAXN];
int b[MAXN],num[MAXN],cntt[MAXN],c=1;
bool cmp(Node n,Node m)
{
    return n.val<m.val;
} 
void hash(int n)
{
    sort(t+1,t+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        if(num[c]!=t[i].val) b[t[i].id]=++c,num[c]=t[i].val;
        else b[t[i].id]=c;
    }
}
//骨架树qwq
struct node
{
    int l,r,ls,rs,sum;
    node()
    {
        l=r=ls=rs=sum=0;    
    }   
}a[MAXN<<5];
int cnt=0,root[MAXN];
void update(int k){a[k].sum=a[a[k].ls].sum+a[a[k].rs].sum;}
int build_bone(int l,int r)
{
    cnt++;
    int op=cnt;
    a[op].l=l,a[op].r=r;
    int mid=(l+r)>>1;
    if(l==r)
    {
        a[op].sum=0;
        return op;//根的左右儿子都是0 
    }
    a[op].ls=build_bone(l,mid),a[op].rs=build_bone(mid+1,r);
    update(op);
    return op;
}
//可持久化  
int build_chain(int k,int cur,int x)//对k点可持久化成x 
{
    cnt++;
    int op=cnt;
    a[op].l=a[cur].l,a[op].r=a[cur].r;
    int mid=(a[cur].l+a[cur].r)>>1;
    if(a[cur].l==a[cur].r)
    {
        a[op].sum=x;
        return op;
    }
    if(k<=mid) a[op].ls=build_chain(k,a[cur].ls,x),a[op].rs=a[cur].rs;
    else if(k>mid) a[op].rs=build_chain(k,a[cur].rs,x),a[op].ls=a[cur].ls;
    update(op);
    return op;
}
//查询第x小值 
int query(int k1,int k2,int x)
{
    if(a[k1].l==a[k1].r) return num[a[k1].l];
    int mid=a[a[k1].ls].sum-a[a[k2].ls].sum;
    if(x<=mid) return query(a[k1].ls,a[k2].ls,x);
    else if(x>mid) return query(a[k1].rs,a[k2].rs,x-mid);
}
int n,m,k,l,r;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&t[i].val),t[i].id=i;
    hash(n);
    root[0]=build_bone(1,c); 
    for(int i=1;i<=n;i++) cntt[b[i]]++,root[i]=build_chain(b[i],root[i-1],cntt[b[i]]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",query(root[r],root[l-1],k));  
    } 
    return 0;
} 

这是主席树经典的静态区间第\(k\)小值问题,当然还有一个板子,我\(A\)了会再写写的(背过板子就好了)

Guess you like

Origin www.cnblogs.com/tlx-blog/p/12356899.html