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\n",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)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM