题意:给你一棵树,每个点有一定高度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)可以完美解决
题解:线段树+-1后缀和+贪心
给每一条链上的最小值所在位置+1,每一个si所在位置-1,线段树维护后缀和(实现时是前缀加),当后缀和都>=0,合法。
基于贪心从后往前找到第一个不合法位置k,枚举树上所有点看其权值变成k是否可行。当某个点的权值改变时,有影响的链是其为链上最小值的链,该点变成k后链上最小值为min(次大,k),更新线段树,看此时Min是否>=0,合法则统计最小代价,还原。
[为什么找到最大的一个不合法位置就行了?1.若要使得该不合法人找到对应点,必然使得一个点的高度至少增加到k。2.更改的这些链,若次大<k,那么比k大无用。若次大>k,即链上最小值变成k,k>剩下所有人的高度,必然满足。]
时间复杂度O(nlogn)。