• *Level Up HDU


    题意:

      公司内有 (n) 名员工,除了老板之外,每个人都有一个直接的主管。老板的编号是 (1)。每个人都有能力价值 (A_i)。一个人 (i) 的工资定义为他和其下属(直接和非直接下属)工资的中位数。但公司会选择一个人,使其工资为 (100000)。求出所有人工资总和的最大值。
    当总数为 (t) 时,中位数是第 (lceil frac{t}{2} ceil)小的数。
    数据范围:
    (1≤n≤100000)
    (1≤A_i≤100000)
    (1≤P_i<i)

    分析:

      首先看到求第几小的数,就想到了主席树,而且看数据范围,连离散化都不用。
      接着,通过(dfs)序把树拍平,记下每个点的起始位置和所在子树的大小就可以把其所有孩子包括其中。然后,我们考虑,什么时候一个点的子树的中位数会改变。注意当其子树中的一个点变成(100000),并且该点原来的值小于中位数。所以,我们可以枚举每个点,看哪些父亲节点会受到影响而改变工资,求出对应的工资的总和。先求出原始的工资和,然后求出最大的改变值。刚才采用递归的过程进行:从根节点向下,利用树状数组区间求和。但是,因为树状数组求得是前缀和,所以可以该点的值 (x)(100001-x)来代替,就可以把位置变换到前面。对于受影响的父亲节点,其工资会变成原始中位数的下一个数,所以在该位置附上差值,当遍历完子树,要把点清空。

    代码:

    (l) 写成了 (1)(re) 了几次。

    #include <bits/stdc++.h>
    #define pb push_back
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    const int maxn=1e5;
    vector<int>pic[N];
    ll tree[20*N],bit[N],md;
    int lson[20*N],rson[20*N],root[N],cnt;
    int sz[N],dfn[N],a[N],mv[N],d[N];
    int build(int l,int r)
    {
        int t=++cnt;
        tree[t]=0;
        if(l==r)
            return t;
        int mid=(l+r)>>1;
        lson[t]=build(l,mid);
        rson[t]=build(mid+1,r);
        tree[t]=tree[lson[t]]+tree[rson[t]];
        return t;
    }
    int update(int l,int r,int pos,int rt)
    {
        int t=++cnt;
        lson[t]=lson[rt];
        rson[t]=rson[rt];
        if(l==r)
        {
            tree[t]=tree[rt]+1;
            return t;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)
            lson[t]=update(l,mid,pos,lson[rt]);
        else
            rson[t]=update(mid+1,r,pos,rson[rt]);
        tree[t]=tree[lson[t]]+tree[rson[t]];
        return t;
    }
    int query(int u,int v,int l,int r,int k)
    {
        if(l==r)
            return l;
        int mid=(l+r)>>1,num=tree[lson[v]]-tree[lson[u]];
        if(k<=num)
            return query(lson[u],lson[v],l,mid,k);
        else
            return query(rson[u],rson[v],mid+1,r,k-num);
    }
    void add(int p,int val)
    {
        while(p<=maxn)
        {
            bit[p]+=val;
            p+=(p&-p);
        }
    }
    ll sum(int p)
    {
        ll res=0;
        while(p>0)
        {
            res+=bit[p];
            p-=(p&-p);
        }
        return res;
    }
    void dfs(int v,int &num)
    {
        dfn[v]=++num;
        sz[v]=1;
        root[num]=update(1,maxn,a[v],root[num-1]);//遍历的前一个点基础上
        for(int i=0;i<pic[v].size();i++)
        {
            int u=pic[v][i];
            dfs(u,num);
            sz[v]+=sz[u];
        }
    }
    void solve(int v)//*
    {
        add(maxn-mv[v]+1,d[v]);//把在后面的数放在前面
        md=max(md,sum(maxn-a[v]+1));//会影响哪些父亲节点
        for(int i=0;i<pic[v].size();i++)
            solve(pic[v][i]);
        add(maxn-mv[v]+1,-d[v]);//消除
    }
    void init(int n)
    {
        for(int i=1;i<=n;i++)
            pic[i].clear();
        cnt=0,md=0;
        memset(bit,0,sizeof(bit));
    }
    int main()
    {
        int n,u;
        while(scanf("%d",&n)!=EOF)
        {
            init(n);
            for(int i=1;i<=n;i++)
                scanf("%d",&a[i]);
            for(int i=2;i<=n;i++)
            {
                scanf("%d",&u);
                pic[u].pb(i);
            }
            root[0]=build(1,maxn);
            int num=0;
            dfs(1,num);//建主席树
            ll ans=0;
            for(int i=1;i<=n;i++)
            {
                int mid=(sz[i]+1)>>1;//取上界
                if(sz[i]==1)
                {
                    mv[i]=a[i];
                    d[i]=maxn-a[i];
                }
                else
                {
                    mv[i]=query(root[dfn[i]-1],root[dfn[i]+sz[i]-1],1,maxn,mid);//原始工资
                    d[i]=query(root[dfn[i]-1],root[dfn[i]+sz[i]-1],1,maxn,mid+1)-mv[i];//更新后的工资
                }
                ans+=mv[i];
            }
            solve(1);
            printf("%lld
    ",ans+md);
        }
        return 0;
    }
    
    
  • 相关阅读:
    表单提交textarea内容,第一次获取不到值,第二次才能获取到的解决方法:
    连接oracle数据库报错:TNS-12516 TNS:listener could not find available handler with matching protocol stack解决方法
    【BZOJ 1272】 1272: [BeiJingWc2008]Gate Of Babylon (容斥原理+卢卡斯定理)
    【BZOJ 3456】 3456: 城市规划 (NTT+多项式求逆)
    【BZOJ 4332】 4332: JSOI2012 分零食 (FFT+快速幂)
    【BZOJ 4555】 4555: [Tjoi2016&Heoi2016]求和 (NTT)
    【BZOJ 4503】4503: 两个串 (FFT)
    【BZOJ 3771】 3771: Triple (FFT+容斥)
    【BZOJ 3160】 3160: 万径人踪灭 (FFT)
    【UOJ 34】 #34. 多项式乘法 (FFT)
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/12520499.html
Copyright © 2020-2023  润新知