P5290 [十二省联考2019]春节十二响

  考试的时候,本来想拿60的贪心,但是只拿了15……很不开心!

  不过现在知道正解了qwq……

  对于每个点,都开一个优先队列,这个优先队列里的值,代表这个点的子树分成的若干个集合中最大的值。

  那么我们对于一个没有处理的点,分别枚举每一个子树,分别合并每一个优先队列,最后再加入这个点,得到新的优先队列。

  对于正确性,由于每一个点都会单独加入优先队列,所以这个点和子树中的任何一个点都不是同一集合,满足了题意,并且每一层的合并肯定是最优,大的和大的合并。

  对于时间,由于每一个点都会被进行上述操作,所以 n * ,对于枚举每个儿子的优先队列,视为 logn ,删除 logn ,所以复杂度O(n * log2n)。

  考试没想出来……呜呜呜

#include<cstdio>
#include<iostream>
#include<queue>
#define MogeKo qwq
#define ll long long
using namespace std;
const int maxn = 2e5+10;

int n,f,cnt;
ll w[maxn],tem[maxn],id[maxn];
ll ans;
int head[maxn],to[maxn],nxt[maxn];
priority_queue <ll> q[maxn];

void add(int x,int y)
{
    to[++cnt] = y;
    nxt[cnt] = head[x];
    head[x] = cnt;
}

void dfs(int u)
{
    id[u] = u;
    for(int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        dfs(v);
        if(i == head[u])
        {
            id[u] = id[v];
            continue;
        }
        if(q[id[v]].size() > q[id[u]].size()) swap(id[u],id[v]);
        int cnt = 0;
        while(!q[id[v]].empty())
        {
            tem[++cnt] = max(q[id[u]].top(),q[id[v]].top());
            q[id[u]].pop();
            q[id[v]].pop();
        }
        for(int i = 1; i <= cnt; i++)
            q[id[u]].push(tem[i]);
    }
    q[id[u]].push(w[u]);
}

int main()
{
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
        scanf("%lld",&w[i]);
    for(int i = 1; i <= n-1; i++)
    {
        scanf("%d",&f);
        add(f,i+1);
    }
    dfs(1);
    while(!q[id[1]].empty())
    {
        ans += q[id[1]].top();
        q[id[1]].pop();
    }
    printf("%lld",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/popo-black-cat/p/10691768.html