zoukankan      html  css  js  c++  java
  • bzoj4919: [Lydsy1706月赛]大根堆

    题目描述:

    给定一棵 $n$ 个节点的有根树,编号依次为 $1$ 到 $n$ ,其中1号点为根节点。每个点有一个权值  $v_i$ 。
    你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么 $v_i>v_j$ 。
    请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。

    算法标签:主席树,启发式合并

    思路:

    这道题相当于在树上的最长上升子序列,考虑父亲与孩子和同一个父亲的孩子之间对答案的影响。

    将权值离散化,在每一个节点建成权值为下标的线段树。

    首先考虑父亲与孩子,选中这个孩子的答案可以对于权值大于自己的父亲造成贡献,所以我们在线段树中在 $[val[x]+1,mx]$ 这个区间更新当前答案。

    孩子之间没有影响,所以我们直接对应权值的答案相加即可。

    以下代码:

    #include<bits/stdc++.h>
    #define il inline
    #define _(d) while(d(isdigit(ch=getchar())))
    using namespace std;
    const int N=2e5+5;
    int n,val[N],rt[N],head[N],ne[N],to[N],cnt,b[N],nt,tot;
    struct node{
        int l,r,num;
    }t[N*40];
    il int read(){
        int x,f=1;char ch;
        _(!)ch=='-'?f=-1:f;x=ch^48;
        _()x=(x<<1)+(x<<3)+(ch^48);
        return f*x;
    }
    il void ins(int x,int y){
        ne[++cnt]=head[x];
        head[x]=cnt;to[cnt]=y;
    }
    il void modify(int &x,int y,int l,int r){
        if(!x||!y){x=x+y;return;}
        t[x].num+=t[y].num;
        if(l==r)return;int mid=(l+r)>>1;
        modify(t[x].l,t[y].l,l,mid);
        modify(t[x].r,t[y].r,mid+1,r);
    }
    il void insert(int &x,int l,int r,int ql,int qr){
        if(!x)x=++nt;
        if(ql<=l&&r<=qr){t[x].num++;return;}
        int mid=(l+r)>>1;
        if(ql<=mid)insert(t[x].l,l,mid,ql,qr);
        if(mid<qr)insert(t[x].r,mid+1,r,ql,qr);
    }
    il int query(int x,int l,int r,int pos){
        if(!x||!pos)return 0;
        if(l==r)return t[x].num;int mid=(l+r)>>1;
        if(pos<=mid)return t[x].num+query(t[x].l,l,mid,pos);
        return t[x].num+query(t[x].r,mid+1,r,pos);
    }
    il void dfs(int x){
        for(int i=head[x];i;i=ne[i]){
            dfs(to[i]);
            if(t[rt[to[i]]].num>t[rt[x]].num)swap(rt[x],rt[to[i]]);
            modify(rt[x],rt[to[i]],1,tot);
        }
        int res=query(rt[x],1,tot,val[x]-1)+1;
        if(query(rt[x],1,tot,val[x])>=res)return;
        int l=val[x],r=tot,ret=l;
        while(l<=r){
            int mid=(l+r)>>1;
            if(query(rt[x],1,tot,mid)<res)l=mid+1,ret=mid;
            else r=mid-1;
        }
        insert(rt[x],1,tot,val[x],ret);
    }
    int main()
    {
        n=read();val[1]=b[1]=read();read();
        for(int i=2;i<=n;i++){
            b[i]=val[i]=read();ins(read(),i);
        }
        sort(b+1,b+1+n);tot=unique(b+1,b+1+n)-b-1;
        for(int i=1;i<=n;i++)val[i]=lower_bound(b+1,b+1+tot,val[i])-b;
        dfs(1);
        printf("%d
    ",query(rt[1],1,tot,cnt));
        return 0;
    }
    View Code
  • 相关阅读:
    哈希表(hash)
    并查集
    trie树(字典树)
    单调队列(滑动窗口)
    单调栈
    用数组实现栈与队列
    数组实现双链表
    数组实现单链表
    区间合并
    离散化
  • 原文地址:https://www.cnblogs.com/Jessie-/p/10443029.html
Copyright © 2011-2022 走看看