/* 返回顶部 */

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

qwq

题目大意:给出一棵树,要求把这棵树分为若干个集合,集合中不能包含祖先-后代关系,使每个集合中的最大值之和最小。

- 25分

subtask里有一种链的情况,且1不一定是链的一个端点。也就是说,树是一个根节点连着两条链。

假设两条链的点数分别为$m,n(m>n)$。这种情况里,前$n$个点可以两两分为一个集合,后$(m-n)$个点和根节点各自独立为一个集合。

所以,可以把两条链看作两个优先队列,每次比较它们的$top$,把小的向大的合并为一条链,最后再加上根节点。

答案即为$sigma(i=2$~$n)max(w[mi],w[ni] + sigma(i=m-n+1$~$m)w[mi] + w[1]$

考场上半个小时码完了qwq

然而不知道当时怎么推的,举例的时候以为这个思路是错的就没往下想

- 60分

其实这种做法可以由特殊推广到一般。

对于一个有若干个子链的点,可以把这些链两两合并,成为一条链。树上的每个子树,都可以用这种方法合并为一条链。

所以每个节点都开一个优先队列,对于一个根节点,首先遍历它的每一棵子树(链),将这条链($q[v]$)与当前根节点所连的最大链(记为$q[u]$)比较,若$q[v].size>q[u].size$则交换。

将较小的链($q[v]$)合并到较大的链($q[u]$)。遍历所有子树后,将根节点加入$q[u]$。

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

- 100分

这种做法是很麻烦的。想象一条链的情况,需要每次比较儿子和父亲,交换儿子和父亲,把父亲加入队列…

因为最下面的儿子是最先遍历到的,所以可以看作把父亲不停地加入儿子的链。

每次直接把第一个儿子作为最大链;记录每个点的编号,把儿子的编号传递给父亲

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;
        }
       ……
    }
    q[id[u]].push(w[u]);
}

这样就快很多啦w

代码如下

#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;
}
View Code

 

不知道为啥Luogu把这题评成黑的...明明就queue+一个dfs,才60行代码...

 

posted @ 2019-04-11 19:41  Mogeko  阅读(262)  评论(1编辑  收藏  举报