codeforces533A Berland Miners -- 线段树

离散。先求出每个点到根路径上 hi 的最小值、次小值,记为 bi,ci
开一个桶,将每个 bi 在桶中的位置加 1 ,每个si在桶中的位置减 1
那么只有当所有后缀和不小于0时满足条件。
如果要增加一个点的高度,先从大到小枚举桶中的元素,找到第一个小于 0 的位置k,那么最后一定是将一个点的 hi 增加到 k <script type="math/tex" id="MathJax-Element-1706">k</script>。
用线段树维护后缀和的最小值,枚举这个点,更新线段树,如果满足条件就更新答案。
怎么更新线段树呢?容易看出更新一个点只会影响若干条链,区间加就可以了。
具体可以看代码。

代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    if(p1==p2){
        p2=(p1=buf)+fread(buf,1,100000,stdin);
        if(p1==p2)return EOF;
    }
    return *p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
#define N 500010
#define INF 1e9
struct Edge{
    int t,nx;
}e[N<<1];
struct Node{
    int f,w;
}L[N<<1];
vector<int>g[N];
int i,j,k,n,m,x,y,Cnt,M,h[N],t[N<<1],c[N<<2],p[N<<2],c1[N],c2[N],a[N],b[N],w[N<<1],l,r,Ans=INF,f[N];
inline int Min(int x,int y){
    return x<y?x:y;
}
inline bool Cmp(int x,int y){
    return x>y;
}
inline bool Cmp1(Node x,Node y){
    return x.w<y.w;
}
inline void Add(int x,int y){
    e[++Cnt].t=y;e[Cnt].nx=h[x];h[x]=Cnt;
}
inline void Dfs(int x,int y){
    if(a[x]<=a[c1[y]])c1[x]=x,c2[x]=c1[y];else
    if(a[x]<a[c2[y]])c1[x]=c1[y],c2[x]=x;else
    c1[x]=c1[y],c2[x]=c2[y];
    for(int i=h[x];i;i=e[i].nx)
    if(e[i].t!=y)Dfs(e[i].t,x);
}
inline int Find(int x){
    int l=1,r=m,Mid,Ans=1;
    while(l<=r){
        Mid=l+r>>1;
        if(b[Mid]>x)Ans=Mid,l=Mid+1;else r=Mid-1;
    }
    return Ans;
}
inline void Down(int x){
    c[x<<1]+=p[x];
    p[x<<1]+=p[x];
    c[x<<1|1]+=p[x];
    p[x<<1|1]+=p[x];
    p[x]=0;
}
inline void Build(int x,int l,int r){
    if(l==r){
        c[x]=t[l];
        return;
    }
    int Mid=l+r>>1;
    Build(x<<1,l,Mid);
    Build(x<<1|1,Mid+1,r);
    c[x]=Min(c[x<<1],c[x<<1|1]);
}
inline void Update(int x,int l,int r,int L,int R,int y){
    if(l>R||r<L)return;
    if(l>=L&&r<=R){
        c[x]+=y;
        p[x]+=y;
        return;
    }
    if(p[x])Down(x);
    int Mid=l+r>>1;
    Update(x<<1,l,Mid,L,R,y);
    Update(x<<1|1,Mid+1,r,L,R,y);
    c[x]=Min(c[x<<1],c[x<<1|1]);
}
int main(){
    Read(n);
    for(i=1;i<=n;i++)Read(L[i].w),L[i].f=i;
    for(i=1;i<n;i++)Read(x),Read(y),Add(x,y),Add(y,x);
    Read(m);
    for(i=1;i<=m;i++)Read(L[n+i].w),L[n+i].f=n+i;
    sort(L+1,L+n+m+1,Cmp1);
    if(L[1].f>n)b[L[1].f-n]=M=1;else a[L[1].f]=M=1;w[1]=L[1].w;
    for(i=2;i<=n+m;i++)
    if(L[i].w==L[i-1].w)if(L[i].f>n)b[L[i].f-n]=M;else a[L[i].f]=M;else{
        w[++M]=L[i].w;
        if(L[i].f>n)b[L[i].f-n]=M;else a[L[i].f]=M;
    }
    sort(b+1,b+m+1,Cmp);
    a[0]=INF;
    Dfs(1,0);
    for(i=1;i<=n;i++)t[a[c1[i]]]++;
    for(i=1;i<=m;i++)t[b[i]]--;
    for(i=M-1;i;i--)t[i]+=t[i+1];
    for(k=M;k;k--)if(t[k]<0)break;
    if(!k){
        printf("0\n");
        return 0;
    }
    Build(1,1,M);
    for(i=1;i<=n;i++)
    g[c1[i]].push_back(Min(a[c2[i]],k));
    for(i=1;i<=n;i++)
    if(a[i]<k){
        for(j=0;j<g[i].size();j++)
        if(a[i]+1<=g[i][j])Update(1,1,M,a[i]+1,g[i][j],1);
        if(c[1]>=0)Ans=Min(Ans,w[k]-w[a[i]]);
        for(j=0;j<g[i].size();j++)
        if(a[i]+1<=g[i][j])Update(1,1,M,a[i]+1,g[i][j],-1);
    }
    if(Ans==INF)printf("-1\n");else printf("%d\n",Ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值