題意:給你一棵樹,每個點有一定高度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\n",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)。