zoukankan      html  css  js  c++  java
  • P5290 [十二省联考2019]春节十二响

    传送门

    考虑一个子树里是怎么划分的,维护划分出来的每个集合的最大值,这个可以用一个 $multiset$ 维护

    设 $S[x]$ 表示节点 $x$ 的子树中,最优划分 划分出来的每个块的节点最大值

    首先叶子节点的集合显然只有它本身

    然后考虑子树之间的合并,设两个子树根节点为 $x,y$,因为两个子树之间一定不会有祖先后代关系

    贪心地想,显然 $S[x]$ 的最大值优先跟 $S[y]$ 的最大值合并(取 $max$),然后次大值跟次大值合并...这样一路合并下去是最优的

    所以直接启发式合并就好了

    $x$ 的子树合并完后还要考虑当前节点 $x$ 也加入进来,显然此节点只能单独分一个块出来

    一开始以为复杂度 $O(nlog^2_n)$(启发式合并 $nlog_n$,$multiset$ 单次操作 $log_n$)

    但是经过对代码的分析可以发现,这并不是直接合并,而是小的 $S$ 和大的取一个最大值后就没了,并没有增加大的集合的集合大小

    所以每个节点只会算一次,复杂度 $O(nlog_n)$

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<set>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0; char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x;
    }
    const int N=4e5+7;
    int fir[N],from[N<<1],to[N<<1],cntt;
    inline void add(int a,int b)
    {
        from[++cntt]=fir[a]; fir[a]=cntt;
        to[cntt]=b;
    }
    int n,val[N];
    multiset <int> S[N];
    multiset <int>::iterator it,pit,itt;
    inline void merge(int x,int y)//很多细节的启发式合并操作
    {
        if(S[x].size()<S[y].size()) swap(S[x],S[y]);//启发式合并
        if(!S[y].size()) return;//记得特判,不然后面it--时会直接GG
        pit=S[x].end(); pit--; bool flag=1;
        it=S[y].end(); it--;
        while(it!=S[y].begin())
        {
            pit--; itt=pit; pit++;
            //注意要先用itt存一下pit-1的指针等等erase(pit)后pit就是指向不合法地址的指针了,那时再pit--就GG了
            if((*it)>(*pit)) S[x].erase(pit),S[x].insert(*it);
            it--; pit=itt;
        }
        if((*it)>(*pit)) S[x].erase(pit),S[x].insert(*it);//注意最后S[y].begin()的值还没合并
    }
    void dfs(int x)
    {
        for(int i=fir[x];i;i=from[i])
            dfs(to[i]),merge(x,to[i]);
        S[x].insert(val[x]);
    }
    int main()
    {
        n=read(); int a;
        for(int i=1;i<=n;i++) val[i]=read();
        for(int i=2;i<=n;i++)
            a=read(),add(a,i);
        dfs(1);
        ll ans=0;
        for(it=S[1].begin();it!=S[1].end();it++) ans+=(*it);
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    父div的透明度不影响子div透明度
    vue-组件命名
    前端页面优化技巧
    Webstorm添加新建.vue文件功能并支持高亮vue语法和es6语法
    防止被坑
    vue安装教程总结
    vue找错
    前段进阶之路
    VM4061 layui.js:2 Layui hint: form is not a valid module
    三月十一号
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10682961.html
Copyright © 2011-2022 走看看