[CF627D]Preorder Test
题目大意:
一个$n(n\le2\times10^5)$个结点的树,每个结点有一个权值$w_i$。可以任选一点为根,并选择一些结点交换其子结点的顺序,使得该树DFS序上第$m$个结点的权值最大。求最大权值。
思路:
二分答案$k$ ,树形DP检验可行性。
对于以结点$x$为根的子树,用$f[x]$表示$x$的子树经过任意变换的所有DFS序中,满足$w_i\ge k$的最长前缀长度。
若$w_x<k$,则$f[x]$显然为$0$。
若$w_x\ge k$,则对于$x$的每个子结点$y$,若$f[y]=size[y]$,将$y$的子树插入到$x$子树DFS序的最前面仍然是合法的;而对于所有满足$f[y]<size[y]$的子结点$y$,可以选择一个$f[y]$最大的加入。考虑以$x$为根的情况,则只需要在$f[x]$上加上满足$f[y]<size[y]$的$f[y]$第二大的$f[y]$。此时不需要考虑加上的会是$x$上方的点,因为加上$x$上方的点的情况肯定能在$x$上方的点处统计到。
时间复杂度$O(n)$。
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 #include<forward_list> 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const int N=2e5+1; 13 int w[N],f[N],k,size[N],ans,root; 14 std::forward_list<int> e[N]; 15 inline void add_edge(const int &u,const int &v) { 16 e[u].push_front(v); 17 e[v].push_front(u); 18 } 19 void dfs(const int &x,const int &par) { 20 f[x]=size[x]=1; 21 int max1=0,max2=0; 22 for(register auto &y:e[x]) { 23 if(y==par) continue; 24 dfs(y,x); 25 size[x]+=size[y]; 26 if(f[y]==size[y]) { 27 f[x]+=f[y]; 28 } else { 29 if(f[y]>max1) std::swap(f[y],max1); 30 if(f[y]>max2) std::swap(f[y],max2); 31 } 32 } 33 f[x]=w[x]<k?0:f[x]+max1; 34 ans=std::max(ans,f[x]+max2); 35 } 36 inline int calc() { 37 dfs(root,ans=0); 38 return ans; 39 } 40 int main() { 41 const int n=getint(),m=getint(); 42 for(register int i=1;i<=n;i++) w[i]=getint(); 43 for(register int i=1;i<n;i++) { 44 add_edge(getint(),getint()); 45 } 46 root=std::min_element(&w[1],&w[n]+1)-w; 47 int l=*std::min_element(&w[1],&w[n]+1); 48 int r=*std::max_element(&w[1],&w[n]+1); 49 while(l<=r) { 50 k=(l+r)>>1; 51 if(calc()>=m) { 52 l=k+1; 53 } else { 54 r=k-1; 55 } 56 } 57 printf("%d\n",l-1); 58 return 0; 59 }