bzoj 2799 [Poi2012]Salaries 贪心

本文介绍了一种针对树形结构的数据处理算法,通过维护子树内未确定权值的节点数量以及已分配权值的位置,实现了从根节点到叶子节点的权值分配过程。该算法使用栈来管理待分配的权值,并确保权值能够按照特定条件正确地分配给子树内的节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

感觉自己语文水平捉鸡,粘一下吴大爷的题解吧。。。

我们维护以每一个节点为根的子树内部有多少个没有确定权值的点,另外,我们利用一个数组维护每个权值已经给了哪个节点.
我们从小到大遍历每个权值,若这个权值当前并没有确定给哪个节点,则将其压入栈中;否则我们在对应节点的子树中进行一系列确定操作:
我们维护栈中有多少个权值,以及有多少个自由权值.(这个是什么一会再说)
若未确定的节点数正好等于当前的自由权值数与栈中的权值总数,显而易见这些权值都应该分配给这棵子树.但是只有只有一个儿子的情况才能确定这个权值.所以我们不断在链上去找即可.这个过程可能确定了一些权值,随后我们将栈清空,并令自由权值的数目为0.因为他们只能被拘束在这颗子树内,而不能去别的地方.
若不然,我们将自由权值及栈中的权值分配给这棵子树所需要的数目,剩下的全部变成自由权值.
我们发现栈中的权值和自由权值很像,但是为什么不都搞成自由权值,而是非要维护一个栈呢?
这个问题显而易见:自由权值都是可以互相交换的(等价的),而栈中的元素显然不是!

#include <bits/stdc++.h>
using namespace std;
#define N 1000010
int n,root,top;
vector<int>vec[N],pos[N];
int val[N],fa[N],use[N],size[N],st[N];
queue<int>que;
void get(int x)
{
    while(top)
    {   
        val[x]=st[top--];
        if(vec[x].size()!=1)return;
        x=vec[x][0];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&fa[i],&val[i]);
        if(fa[i]==i)root=i,val[i]=n;
        else vec[fa[i]].push_back(i);
        use[val[i]]=1;
    }
    que.push(root);
    while(!que.empty())
    {
        int t=que.front();que.pop();
        st[++top]=t;size[t]=1;
        for(int i=0;i<vec[t].size();i++)
            que.push(vec[t][i]);
    }
    for(int i=top;i>=1;i--)
    {
        for(int j=0,t;j<vec[st[i]].size();j++)
        {
            size[st[i]]+=size[t=vec[st[i]][j]];
            if(!val[t])
                pos[val[st[i]]].push_back(t);
        }
    }
    int cnt=0,p,rem=0,sum=0;
    top=0;
    for(int i=1;i<=n;i++)
    {
        if(!use[i])st[++top]=i;
        else
        {
            int t=pos[i].size();
            cnt+=t;if(t)p=pos[i][0];
            for(int j=0;j<t;j++)
                sum+=size[pos[i][j]];
            if(!use[i+1])
            {
                if(!cnt)continue;
                rem=top+rem-sum;
                if(cnt==1&&!rem)get(p);
                sum=0;cnt=0;top=0;
            }
        }
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",val[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值