[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 }

 

posted @ 2018-05-03 18:12  skylee03  阅读(226)  评论(0编辑  收藏  举报