Scx117
只一眼,便辽阔了时间。

题意:给你一棵树,每个点有一定高度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)。

posted on 2018-06-12 19:07  Scx117  阅读(1017)  评论(0编辑  收藏  举报