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


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

    考虑一个simple的情形:假如一个点有两条链作为儿子,应该怎么样才好?

    明显,同一条链上的点不能在一起,于是链上的一个点只能与另一条链上的点匹配。明显匹配应该从大往小配(两个大的配了,这样最终便少了一个较大的)。于是我们用两个堆记录两条链,每次匹配堆顶的两个元素即可。

    现在考虑从叶子开始处理。对于一个只有叶子作为儿子的节点,显然可以依照上述方法将叶子合在一起;接着,考虑该点的最深的有不止一个儿子的祖先。显然,因为我们合并了叶子,所以该点的儿子一定是数条链(如果不是链的话,我们要么可以合并叶子形成链,要么可以找到更深的使得儿子是不止一条链的节点)。不妨从三条链的情形开始考虑。

    显然,对于该点子树外的点来说,子树内的情形并无影响,反正要么是与该点匹配(但这与合并链无关),要么是与三条链中每条上至多一个点匹配,而如果三条链上的点已经互相有了配对关系也无影响,只需要与配对完后剩下的那个点配对即可。接着,考虑第三条链上的点,同理,其只能与前两条链上至多一个点匹配,也是预先匹配并无影响。这样,我们就可以先合并前两条链,再合并第三条链,最终得到一条大链。这样子,对于拥有众多链作为儿子的节点,一定可以全部并作一条大链,不断并下去,最终会得到唯一一条链,而这条链就是答案。

    可以使用启发式合并堆来处理上述过程。

    时间复杂度 \(O(n\log n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[200100],fa[200100],id[200100];
    priority_queue<int>q[200100];
    long long res;
    void merge(int&x,int y){
    	if(q[x].size()<q[y].size())swap(x,y);
    	vector<int>v;
    	while(!q[y].empty())v.push_back(max(q[x].top(),q[y].top())),q[x].pop(),q[y].pop();
    	while(!v.empty())q[x].push(v.back()),v.pop_back();
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]),id[i]=i;
    	for(int i=2;i<=n;i++)scanf("%d",&fa[i]);
    	for(int i=n;i;i--)q[id[i]].push(a[i]),merge(id[fa[i]],id[i]);
    	while(!q[id[1]].empty())res+=q[id[1]].top(),q[id[1]].pop();
    	printf("%lld\n",res);
    	return 0;
    } 
    

  • 相关阅读:
    em与rem之间的区别以及移动设备中的rem适配方案
    关于两个DIV之间的空白字符
    Bootstrap基本模板
    js 裁剪
    记一次诡异的bug
    Node切换版本
    git 撤销
    使用 iframe + postMessage 实现跨域通信
    <el-input>标签限制输入小数点
    vue elementyUI table :点击一行时选中这一行对应的复选框
  • 原文地址:https://www.cnblogs.com/Troverld/p/14612752.html
Copyright © 2020-2023  润新知