• 主席树学习专题


    Count On A Tree

    建立线段树记录每个\([1,u]\)路径上的前缀中在值域\([L,R]\)中的个数

    查询时 计算 \(cnt[x]+cnt[y]-cnt[lca]-cnt[fa[lca]]\)

    const int N=1e5+10,P=1e9+7;
    int n,m;
    int s[N*20],ls[N*20],rs[N*20],T[N];
    int cnt;
    
    
    struct Edge{
        int to,nxt;
    }e[N<<1];
    int head[N],ecnt;
    void AddEdge(int u,int v){
        e[++ecnt]=(Edge){v,head[u]};
        head[u]=ecnt;
    }
    #define erep(u,i) for(int i=head[u];i;i=e[i].nxt)
    
    int fa[N][20];
    
    int ncnt;
    
    void Add(int l,int r,int now,int pre,int x){
        s[now]=s[pre]+1;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) rs[now]=rs[pre],Add(l,mid,ls[now]=++cnt,ls[pre],x);
        else ls[now]=ls[pre],Add(mid+1,r,rs[now]=++cnt,rs[pre],x);
    }
    
    int Que(int l,int r,int a,int b,int lca,int f,int k){
        if(l==r) return l;
        int mid=(l+r)>>1;
        int t=s[ls[a]]+s[ls[b]]-s[ls[lca]]-s[ls[f]];
        if(t>=k) return Que(l,mid,ls[a],ls[b],ls[lca],ls[f],k);
        return Que(mid+1,r,rs[a],rs[b],rs[lca],rs[f],k-t);
    }
    
    int a[N],b[N],dep[N];
    
    void dfs(int u,int f){
        fa[u][0]=f;
        for(int i=1;(1<<i)<=dep[u];i++) fa[u][i]=fa[fa[u][i-1]][i-1];
        Add(1,ncnt,T[u]=++cnt,T[f],a[u]);
        erep(u,i){
            int v=e[i].to;
            if(v==f) continue;
            dep[v]=dep[u]+1;
            dfs(v,u);
        }
    }
    
    int LCA(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=0,del=dep[x]-dep[y];(1<<i)<=del;i++) if(del&(1<<i)) x=fa[x][i];
        if(x==y) return x;
        drep(i,17,0) if(dep[x]>=(1<<i)){
            if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
        }
        return fa[x][0];
    }
    
    
    
    
    int main(){
        cnt=0;
        n=rd(),m=rd();
        rep(i,1,n) a[i]=b[i]=rd();
        sort(b+1,b+n+1);ncnt=unique(b+1,b+n+1)-b-1;
        rep(i,1,n) a[i]=lower_bound(b+1,b+ncnt+1,a[i])-b;
        rep(i,1,n-1) {
            int u=rd(),v=rd();
            AddEdge(u,v),AddEdge(v,u);
        }
        dfs(1,0);
        rep(i,1,m){
            int x=rd(),y=rd(),k=rd();
            int lca=LCA(x,y);
            int ans=Que(1,ncnt,T[x],T[y],T[lca],T[fa[lca][0]],k);
            printf("%d\n",b[ans]);
        }
    }
    
    

    \[\ \]

    \[\ \]

    \[\ \]

    middle

    将权值离散化,对于每一个权值的前缀建立一棵线段树

    对于第一棵树,所有叶子节点的权值为1

    每次将小于这个权值的点的叶子节点更新为-1

    线段树中,每个点\([L,R]\)存的是一段区间的

    1.和

    2.前缀最大

    3.后缀最大

    我们可以由中位数的定义得到一个结论

    如果这个数\(x\)及小于等于它的数可以成为区间\([L,R]\)的中位数

    则有这段区间里大于等于它的数的个数 大于等于 这段区间里小于它的个数

    即在\(tree[x]\)\(sum[L,R]>=0\)

    这样的线段树存储,使我们可以在\([a,b]\)中找到最大的后缀和,在\([c,d]\)找到最大的前缀和,再加上中间这段\([b+1,c-1]\)的和得到最大的可能

    那么我们如何得到这个x呢?

    二分答案呗

    const int N=20005,K=50;
    int n;
    int a[N],B[N],ncnt;
    int T[N];
    
    struct node{
        int lma,rma,s;
    }tr[N*K];
    int ls[N*K],rs[N*K];
    int cnt;
    
    vector <int> V[N];
    
    void Up(node &p,node ls,node rs){
        p.s=ls.s+rs.s;
        p.lma=max(ls.lma,ls.s+rs.lma);
        p.rma=max(rs.rma,ls.rma+rs.s);
    }
    
    
    void Build(int p,int l,int r){
        tr[p]=(node){r-l+1,r-l+1,r-l+1};
        if(l==r) return;
        int mid=(l+r)>>1;
        Build(ls[p]=++cnt,l,mid);
        Build(rs[p]=++cnt,mid+1,r);
    }
    
    void Add(int p,int pre,int l,int r,int x){
        tr[p]=tr[pre];
        if(l==r) { tr[p]=(node){-1,-1,-1} ; return; }
        int mid=(l+r)>>1;
        if(x<=mid) rs[p]=rs[pre],Add(ls[p]=++cnt,ls[pre],l,mid,x);
        else ls[p]=ls[pre],Add(rs[p]=++cnt,rs[pre],mid+1,r,x);
        Up(tr[p],tr[ls[p]],tr[rs[p]]);
    }
    
    
    node Que(int p,int l,int r,int ql,int qr){
        if(l==ql&&r==qr) return tr[p];
        int mid=(l+r)>>1;
        if(qr<=mid) return Que(ls[p],l,mid,ql,qr);
        else if(ql>mid) return Que(rs[p],mid+1,r,ql,qr);
        else {
            node ans;
            Up(ans,Que(ls[p],l,mid,ql,mid),Que(rs[p],mid+1,r,mid+1,qr));
            return ans;
        }
    }
    
    
    bool Check(int k,int a,int b,int c,int d){
        int ans=0;
        if(c-1>=b+1) ans+=Que(T[k],1,n,b+1,c-1).s;
        ans+=Que(T[k],1,n,a,b).rma+Que(T[k],1,n,c,d).lma;
        return ans>=0;
    }
    
    int main(){
        n=rd();
        rep(i,1,n) a[i]=B[i]=rd();
        sort(B+1,B+n+1),ncnt=unique(B+1,B+n+1)-B-1;
        rep(i,1,n) V[a[i]=lower_bound(B+1,B+ncnt+1,a[i])-B].push_back(i);
        int pre;
        Build(pre=T[1]=++cnt,1,n);
        rep(i,2,ncnt){
            T[i]=T[i-1];
            rep(j,0,V[i-1].size()-1) {
                Add(T[i]=++cnt,pre,1,n,V[i-1][j]);
                pre=T[i];
            }
        }
        pre=0;
        rep(kase,1,rd()){
            int tmp[10];
            rep(j,0,3) tmp[j]=(pre+rd())%n+1;
            sort(tmp,tmp+4);
            int l=1,r=ncnt;
            while(l<=r){
                int mid=(l+r)>>1;
                if(Check(mid,tmp[0],tmp[1],tmp[2],tmp[3])) l=mid+1;
                else r=mid-1;
            }
            printf("%d\n",pre=B[l-1]);
        }
        return 0;
    }
    
  • 相关阅读:
    20145227&20145201 《信息安全系统设计基础》实验五
    20145227&20145201 《信息安全系统设计基础》实验四
    20145227《信息安全系统设计基础》第十一周学习总结
    20145206 《信息安全系统设计基础》课程总结
    20145206 《信息安全系统设计基础》第十四周学习总结
    20145206 《信息安全系统设计基础》第十三周学习总结
    20145206 《信息安全系统设计基础》第十二周学习总结
    第五周博客实践分析
    《信息安全系统设计基础》第三周问题总结
    《信息安全系统设计基础》 实验五
  • 原文地址:https://www.cnblogs.com/chasedeath/p/11259363.html
Copyright © 2020-2023  润新知