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

传送门

做题千万条,读题第一条
编程不规范,爆零两行泪

推荐阅读(雾)

考虑一个贪心,就是先把所有点按权值从大到小排序,然后每次考虑能不能和其他已经插进去的点放在一个集合,不能那么答案就加上对应权值.如果我们按照最优策略构造,那么最后首先集合个数是最少的,而且因为尽量把大的元素和更大的放在一起,那么最终答案也是最优的

这个贪心怎么优化呢?注意到两个点可以在一起选,当且仅当这两个点的子树的(dfn)序区间无交,那么每次选点的过程中,如果所有集合都和他无交,那么可以放在其他集合中,否则就必须新开一个集合.那如果加一个点就在对应区间+1,考虑当前这个区间的最大值,他代表有几个集合和当前区间有交,所以如果区间最大值为当前集合个数就要新开一个集合,那么要更新答案.这个可以线段树区间加,区间最大值实现

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db double

using namespace std;
const int N=2e5+10;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
void add(int x,int y)
{
    ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
}
int n,a[N],dfn[N],ti,sz[N];
void dfs(int x)
{
    sz[x]=1,dfn[x]=++ti;
    for(int i=hd[x];i;i=nt[i])
    {
        int y=to[i];
        dfs(y),sz[x]+=sz[y];
    }
}
struct node
{
    int l,r,x;
    bool operator < (const node &bb) const {return x>bb.x;}
}qq[N];
int ma[N<<2],tg[N<<2];
#define mid ((l+r)>>1)
void psup(int o){ma[o]=max(ma[o<<1],ma[o<<1|1]);}
void ad(int o,int x){ma[o]+=x,tg[o]+=x;}
void psdn(int o){if(tg[o]) ad(o<<1,tg[o]),ad(o<<1|1,tg[o]),tg[o]=0;}
void modif(int o,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr){ad(o,1);return;}
    psdn(o);
    if(ll<=mid) modif(o<<1,l,mid,ll,rr);
    if(rr>mid) modif(o<<1|1,mid+1,r,ll,rr);
    psup(o);
}
int quer(int o,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr) return ma[o];
    psdn(o);
    int an=0;
    if(ll<=mid) an=max(an,quer(o<<1,l,mid,ll,rr));
    if(rr>mid) an=max(an,quer(o<<1|1,mid+1,r,ll,rr));
    psup(o);
    return an;
}
LL cn,ans;

int main()
{
    n=rd();
    for(int i=1;i<=n;++i) a[i]=rd();
    for(int i=2;i<=n;++i) add(rd(),i);
    dfs(1);
    for(int i=1;i<=n;++i) qq[i]=(node){dfn[i],dfn[i]+sz[i]-1,a[i]};
    sort(qq+1,qq+n+1);
    for(int i=1;i<=n;++i)
    {
        int ll=qq[i].l,rr=qq[i].r;
        if(quer(1,1,n,ll,rr)>=cn) ++cn,ans+=qq[i].x;
        modif(1,1,n,ll,rr);
    }
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/smyjr/p/10724798.html