一道搞了N天的神仙题
题目大意
你现在有M个货物要从根节点运到树上各节点(每个节点只能放一个),每个节点有高度
h
[
i
]
h[i]
h[i],每个货物都有一个高度
B
[
i
]
B[i]
B[i],所以这个货物经过的所有山洞,都不能低于
B
[
i
]
B[i]
B[i]
你现在可以改变一个山洞的高度,问最少增加多少,使得所有货物都能运出去
题解
-
首先来考虑不开凿情况
设第 i i i个能放进 n u m [ i ] num[i] num[i]个山洞,那么显然满足条件的情况为 ∀ n u m [ i ] > = i \forall num[i]>=i ∀num[i]>=i,即 ∀ n u m [ i ] − i > = 0 \forall num[i]-i>=0 ∀num[i]−i>=0
这东西还是不好维护,再转化一下,就是 m i n ( n u m [ i ] − i ) > = 0 min(num[i]-i)>=0 min(num[i]−i)>=0
⟶ \longrightarrow ⟶这就可以用线段树方便地维护了 -
接下来考虑开凿
我们可以二分枚举每一个节点开凿的高度,然后check上面的条件
当一个 h [ i ] h[i] h[i]改变时,对于 n u m num num的影响是一个区间 [ 1 , k ] [1,k] [1,k],在 B [ i ] B[i] B[i](有序)上二分得出这个k,在线段树上修改,之后再改回来即可
复杂度 O ( N ∗ l o g 2 ) O(N*log^2) O(N∗log2)
然后拼命卡常,仍然卡不进时限。。 -
继续优化
线段树的log基本是优不掉了,那能不能把节点的二分给去掉呢?
考虑一个元素 m a x ( B [ i ] ) ∈ { n u m [ i ] − i < 0 } max(B[i])\in \{num[i]-i<0\} max(B[i])∈{num[i]−i<0}
对于这个东西,在有解的情况下,一定会有一个山洞被改成 B [ i ] B[i] B[i]若改B[k]>B[i],则其他B[k]一定是放满的,对于B[i]没有影响
若改B[k]<B[i],那么只可能让num[i]变得更小,也没用所以,只需要枚举每个节点让它变成 B [ i ] B[i] B[i]即可
复杂度 O ( N ∗ l o g ) O(N*log) O(N∗log)
普通递归式线段树常数巨大,仍然跑得有点慢。。 -
小Trick
我们在构造时要构造出
Min[i]:到根节点路径的最小值
Min_[i]:到根节点路径的次小值(非严格,更改时要用)
id[i]:最小值的节点标号那么我们贪心想法,按 M i n [ i ] Min[i] Min[i]从大到小的顺序枚举节点(此时可行解越来越大)选择
并且只选择id[i]==i且Min_[i]>Min[i]的节点进行尝试——只有这些节点才会真正产生影响
然后就跑得比较快乐了~~
Code略长~~
#include<bits/stdc++.h>
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
#define pt(ch) (Top<1000000?St[Top++]=ch:(Out(),St[(Top=0)++]=ch))
#define Out() (fwrite(St,1,Top,stdout))
#define __R register
#define IL inline
using namespace std;
int Top;static char St[1000000],buf[1000000],*p1=buf,*p2=buf;
const int maxn=(5e5)+5,INF=2e9;
int N,Ans,M,Bas,B[maxn],h[maxn];bool vis[maxn];
struct Vector{
int tot,lnk[maxn],nxt[maxn<<1],son[maxn<<1];
IL void add_e(int x,int y){son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;}
}T,G;
IL int read(){
__R int ret=0;__R char ch=gt();
while(ch<'0'||ch>'9') ch=gt();
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
return ret;
}
void write(int x){if(x>9) write(x/10);pt(x%10+'0');}
//SegmentTree
int tag[maxn<<2],P[maxn<<2],Mid[maxn<<2];
#define Ls (i<<1)
#define Rs (Ls|1)
IL int Min(int x,int y){return x<y?x:y;}
IL void Add(int i,int p){P[i]+=p,tag[i]+=p;}
IL void Down(int i){Add(Ls,tag[i]),Add(Rs,tag[i]),tag[i]=0;}
IL void Updata(int i){P[i]=Min(P[Ls],P[Rs]);}
void Build(int i=1,int L=1,int R=M){
if(L==R) return (void)(P[i]=L-M-1);
Mid[i]=L+R>>1;
Build(Ls,L,Mid[i]),Build(Rs,Mid[i]+1,R),Updata(i);
}
void Modify(int x,int y,int p,int i=1,int L=1,int R=M){
if(x<=L&&R<=y) return (void)(Add(i,p));
if(tag[i]) Down(i);
if(x<=Mid[i]) Modify(x,y,p,Ls,L,Mid[i]);if(y>Mid[i]) Modify(x,y,p,Rs,Mid[i]+1,R);
Updata(i);
}
int Query(int x,int i=1,int L=1,int R=M){
if(L==R) return P[i];
if(tag[i]) Down(i);
return x<=Mid[i]?Query(x,Ls,L,Mid[i]):Query(x,Rs,Mid[i]+1,R);
}
//只要尝试将所有洞改成max(Bi)∈{num[i]-i<0},因为在有解时,一定要有一个洞被凿成这个Bk
void Find(){for(__R int i=M;i;i--) if(Query(i)<0){Bas=B[i];break;}}
//Build
int A1[maxn],A2[maxn],out[maxn],fuk[maxn];
void Dfs(int x,int fa,int id,int Mi,int Mi_){
if(h[x]<=Mi) Mi_=Mi,Mi=h[x],id=x;else if(h[x]<Mi_) Mi_=h[x];
A1[x]=Mi,A2[x]=Mi_,T.add_e(id,x),fuk[x]=id;
for(__R int j=G.lnk[x];j;j=G.nxt[j]) if(G.son[j]!=fa) Dfs(G.son[j],x,id,Mi,Mi_);
}
IL int Calc(int x,int p){
__R int L=1,R=M,mid;
while(L<=R) if(B[mid=L+R>>1]<=x) L=mid+1;else R=mid-1;
if(R>=1) Modify(1,R,p);
return R;
}
int X[maxn],Y[maxn];
IL void Solve(int x){
__R int lst=h[x];h[x]=Bas;
for(__R int j=T.lnk[x];j;j=T.nxt[j]) X[j]=Calc(A1[x],-1),Y[j]=Calc(Min(A2[T.son[j]],h[x]),1);
if(P[1]>=0) Ans=Bas-lst;
for(__R int j=T.lnk[x];j;j=T.nxt[j]){
if(X[j]>=1) Modify(1,X[j],1);
if(Y[j]>=1) Modify(1,Y[j],-1);
}
h[x]=lst;
}
//XG
int len,id[maxn];
struct ff{
int x,y;
IL bool operator <(const ff b)const{return y<b.y;}
}hep[maxn];
IL void Put(int x,int y){hep[++len]=(ff){x,y},push_heap(hep+1,hep+1+len);}
IL ff Get(){return pop_heap(hep+1,hep+1+len),hep[len--];}
IL void ReBuild(){
int cnt=M;len=0,Put(1,h[1]);
while(len&&cnt){
__R ff i=Get();vis[i.x]=1,id[++id[0]]=i.x;
if(B[cnt]>i.y) break;
cnt--;
for(int j=G.lnk[i.x];j;j=G.nxt[j]) if(!vis[G.son[j]]) Put(G.son[j],Min(i.y,h[G.son[j]]));
}
while(len) vis[id[++id[0]]=Get().x]=1;
}
int main(){
N=read();
for(__R int i=1;i<=N;i++) h[i]=read();
for(__R int i=1;i<N;i++){
int x=read(),y=read();
G.add_e(x,y),G.add_e(y,x);
}
if((M=read())>N) return puts("-1"),0;
for(__R int i=1;i<=M;i++) B[i]=read();sort(B+1,B+1+M),Ans=B[M];
Build(),Dfs(1,0,0,INF,INF),ReBuild();
for(__R int i=1;i<=N;i++) Calc(A1[i],1);
if(P[1]>=0) Ans=0;
else{
Find();
for(int i=1,x;i<=id[0];i++) if(x=id[i],Bas>h[x]&&Bas-h[x]<Ans&&fuk[x]==x&&A2[x]>A1[x]) Solve(x);
}
if(Ans<B[M]) printf("%d\n",Ans);else puts("-1");
return Out(),0;
}