• CF533A Berland Miners


    题意:给你一棵树,每个点有一定高度hi。需要选择k个点放进k个人,每个人的高度为si。

    第i个人能够放进x点当且仅当从根到x的路径上的高度最小值>=si。现在你最多只能选择一个节点增加一定的高度使其满足要求,问最小代价?n<=5e5。

    标程:

     1 #include<bits/stdc++.h>
     2 #define mid ((l+r)>>1)
     3 using namespace std;
     4 int read()
     5 {
     6     int x=0;char ch=getchar();
     7     while (ch<'0'||ch>'9') ch=getchar();
     8     while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
     9     return x;
    10 }
    11 const int N=5e5+5;
    12 vector<int> vec[N];
    13 int cnt,head[N],Mn_pos[N],zx[N],cx[N],n,u,v,m,Min[N<<3],tag[N<<3],s[N],h[N],k,t,ans,tot,b[N*2];
    14 const int inf=0x3f3f3f3f;
    15 struct node{int to,next;}num[N*2];
    16 void add(int x,int y)
    17 {num[++cnt].to=y;num[cnt].next=head[x];head[x]=cnt;}
    18 void dfs(int x,int fa)
    19 {
    20     zx[x]=zx[fa];cx[x]=cx[fa];Mn_pos[x]=Mn_pos[fa];
    21     if (h[x]<zx[x]) cx[x]=zx[x],zx[x]=h[x],Mn_pos[x]=x;
    22     else if (h[x]<cx[x]) cx[x]=h[x];
    23     vec[Mn_pos[x]].push_back(x);//vec[i]保存以i为链上最小值的链端点 
    24     for (int i=head[x];i;i=num[i].next)
    25       if (num[i].to!=fa) dfs(num[i].to,x);
    26 }
    27 void down(int k)
    28 {
    29     if (tag[k])
    30     {
    31         tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k];
    32         Min[k<<1]+=tag[k];Min[k<<1|1]+=tag[k];
    33         tag[k]=0;
    34     }
    35 }
    36 void ins(int k,int l,int r,int R,int y)
    37 {
    38     if (r<=R) {Min[k]+=y;tag[k]+=y;return;}
    39     down(k);
    40     if (R>mid) ins(k<<1|1,mid+1,r,R,y);
    41     ins(k<<1,l,mid,R,y);
    42     Min[k]=min(Min[k<<1],Min[k<<1|1]);
    43 }
    44 int Find(int k,int l,int r)
    45 {
    46     if (l==r) return l;
    47     down(k);//这里也要pushdown! 
    48     if (Min[k<<1|1]<0) return Find(k<<1|1,mid+1,r);
    49     else return Find(k<<1,l,mid);
    50 }
    51 void solve(int x,int fa)
    52 {
    53     if (b[h[x]]<b[k])
    54     {
    55         for (int i=0;i<vec[x].size();i++)
    56           t=vec[x][i],ins(1,1,tot,zx[t],-1),ins(1,1,tot,min(cx[t],k),1);
    57         if (Min[1]>=0) ans=min(ans,b[k]-b[h[x]]);
    58         for (int i=0;i<vec[x].size();i++)
    59           t=vec[x][i],ins(1,1,tot,zx[t],1),ins(1,1,tot,min(cx[t],k),-1);
    60     }
    61     for (int i=head[x];i;i=num[i].next)
    62       if (num[i].to!=fa) solve(num[i].to,x);
    63 }
    64 int main()
    65 {
    66     n=read();Min[0]=zx[0]=cx[0]=inf;
    67     for (int i=1;i<=n;i++) h[i]=b[++tot]=read();
    68     for (int i=1;i<n;i++) u=read(),v=read(),add(u,v),add(v,u);
    69     m=read();
    70     for (int i=1;i<=m;i++) s[i]=b[++tot]=read();
    71     sort(b+1,b+tot+1);tot=unique(b+1,b+tot+1)-b-1;
    72     for (int i=1;i<=n;i++)
    73       h[i]=lower_bound(b+1,b+tot+1,h[i])-b,s[i]=lower_bound(b+1,b+tot+1,s[i])-b;
    74     dfs(1,0);
    75     for (int i=1;i<=n;i++)
    76       ins(1,1,tot,s[i],-1),ins(1,1,tot,zx[i],1);
    77     if (Min[1]>=0) return puts("0"),0;
    78     ans=inf;k=Find(1,1,tot);solve(1,-1);
    79     printf("%d
    ",ans==inf?-1:ans);
    80     return 0;
    81 }
    82 //find(1e9)浮点数与库中冲突,换掉find函数名或M=1e9,find(M)可以完美解决 
    View Code

    题解:线段树+-1后缀和+贪心

    给每一条链上的最小值所在位置+1,每一个si所在位置-1,线段树维护后缀和(实现时是前缀加),当后缀和都>=0,合法。

    基于贪心从后往前找到第一个不合法位置k,枚举树上所有点看其权值变成k是否可行。当某个点的权值改变时,有影响的链是其为链上最小值的链,该点变成k后链上最小值为min(次大,k),更新线段树,看此时Min是否>=0,合法则统计最小代价,还原。

    [为什么找到最大的一个不合法位置就行了?1.若要使得该不合法人找到对应点,必然使得一个点的高度至少增加到k。2.更改的这些链,若次大<k,那么比k大无用。若次大>k,即链上最小值变成k,k>剩下所有人的高度,必然满足。]

    时间复杂度O(nlogn)。

  • 相关阅读:
    document.load 和document.ready 两个事件的区别
    undefined和null的区别
    点击返回上一个界面事件
    APP上点击事件时 取消点击的阴影
    MUI控制安卓手机自带的返回键 禁止返回
    css过滤镜实现颜色渐变
    CSS3 Media Queries模板:max-width和min-width
    什么是响应式网页设计?
    实用的CSS3-渐变背景色
    Oracle EBS 报表日期格式问题
  • 原文地址:https://www.cnblogs.com/Scx117/p/9174637.html
Copyright © 2020-2023  润新知