zoukankan      html  css  js  c++  java
  • SCUT106 花式AC 主席树版本

    网址:https://scut.online/p/106

    题意:

    给出一个点权树以$1$为根节点,求子树有几个节点的权值小于等于$k$。

    题解:

    主席树版本,先对树$dfs$求出第一次进入某节点的时间戳,然后同时处理出子树的节点数,然后求出的$dfs$序中某节点的时间戳后长度为该节点的子树的大小$-1$就是这个节点的子树对应的序列,在主席树上求个数即可。原理类似于求区间第k大,只不过是每棵主席树都保存了序列前$m(1 leq m leq n)$个数大小是$[1,n]$数的总个数,然后查询时查询区间中的$[1,k]$的数的个数即可。由于本题数值过大,因为权值是离散化的,所以输入的$k$也要离散化,找到小于等于k的第一个数。

    AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=100005;
    struct cheiftree
    {
        struct node
        {
            int l,r,sum;
        };
        node tr[MAXN*20];
        int rt[MAXN];
        int cnt=0;
        void init()
        {
            cnt=0;
        }
        void build(int &rt,int l,int r)
        {
            rt=++cnt;
            tr[rt].sum=0;
            int m=(l+r)/2;
            if(l==r)
                return;
            build(tr[rt].l,l,m);
            build(tr[rt].r,m+1,r);
        }
        void update(int &rt,int l,int r,int k)
        {
            tr[++cnt]=tr[rt];
            rt=cnt;
            ++tr[rt].sum;
            if(l==r)
                return;
            int m=(l+r)/2;
            if(k<=m)
                update(tr[rt].l,l,m,k);
            else  
                update(tr[rt].r,m+1,r,k);
        }
        int query(int rl,int rr,int l,int r,int val)
        {
            if(l==r)
                return tr[rr].sum-tr[rl].sum;
            int m=(l+r)/2;
            if(val<=m)
                return query(tr[rl].l,tr[rr].l,l,m,val);
            else if(val>m)
                return tr[tr[rr].l].sum-tr[tr[rl].l].sum+query(tr[rl].r,tr[rr].r,m+1,r,val);
        }
    };
    cheiftree tr;
    int in[MAXN],a[MAXN],b[MAXN],size[MAXN],vis[MAXN];
    int cnt=0;
    vector<int>E[MAXN];
    void print()
    {
        for(int i=1;i<=tr.cnt;++i)
            cout<<tr.tr[i].l<<" "<<tr.tr[i].r<<" "<<tr.tr[i].sum<<endl;
    }
    void dfs(int u)
    {
        in[u]=++cnt;
        size[u]=1;
        vis[cnt]=u;
        for(auto &i:E[u])
        {
            dfs(i);
            size[u]+=size[i];
        }
    }
    void init(int n)
    {
        cnt=0;
        for(int i=1;i<=n;++i)
            E[i].clear();
    }
    int main()
    {
        int n,t,m;
        while(~scanf("%d%d",&n,&m))
        {
            init(n);
            for(int i=1;i<n;++i)
            {
                scanf("%d",&t);
                E[t].push_back(i+1);
            }
            dfs(1);
            //for(int i=1;i<=cnt;++i)
                //cout<<in[i]<<" "<<size[i]<<" "<<vis[i]<<endl;
            for(int i=1;i<=n;++i)
            {
                scanf("%d",&a[i]);
                b[i]=a[i];
            }
            sort(b+1,b+1+n);
            int nnew=unique(b+1,b+1+n)-b-1;
            tr.init();
            tr.build(tr.rt[0],1,nnew);
            for(int i=1;i<=n;++i)
            {
                tr.rt[i]=tr.rt[i-1];
                int pos=lower_bound(b+1,b+1+nnew,a[vis[i]])-b;
                tr.update(tr.rt[i],1,nnew,pos);
            }
            //print();
            for(int i=0;i<m;++i)
            {
                scanf("%d%d",&n,&t);
                int k=upper_bound(b+1,b+1+nnew,t)-b-1;
                int l=tr.rt[in[n]-1],r=tr.rt[in[n]+size[n]-1];
                printf("%d
    ",tr.query(l,r,1,nnew,k));
            }
        }
        return 0;
    }
    
  • 相关阅读:
    图片上传功能,前端,后台,完整版代码
    JS 字符串常用操作
    安卓真机运行webview,被撑破,解决方案。
    项目写法,总结
    VUE 的常用
    检查代码的方法
    很牛B的写法,数据库,查询,放在一个方法里面,统一过滤、
    走过的坑
    说说IEnumerable和yield
    关于display: inline-block的间隙问题
  • 原文地址:https://www.cnblogs.com/Aya-Uchida/p/11332737.html
Copyright © 2011-2022 走看看