[atAGC007E]Shik and Travel

二分枚举答案,判定答案是否合法

贪心:每一个叶子只能经过一遍,因此叶子的顺序一定是一个dfs序,即走完一棵子树中的所有叶子才会到子树外

根据这个贪心可以dp,设$f[k][l][r]$表示仅考虑$k$的子树,$l$和$r$为第一个和最后一个叶子到根的距离,时间复杂度大约有$o(n^{4或5})$,无法通过

考虑优化,将所有$f[k][l][r]=1$的$(l,r)$记录下来,若存在$l'le l$且$r'le r$,$(l,r)$一定没有意义,这可以用一个单调栈来维护,那么同一个$l/r$所对应的$r/l$最多只有1个

转移考虑合并,不妨假设左子树比右子树小,枚举左子树中的$(l_{1},r_{1})$,对于右子树中的$(l_{2},r_{2})$,有两种转移的可能:1.若$r_{1}+l_{2}+v_{ls}+v_{rs}le ans$,转移到$(l_{1}+v_{ls},r_{2}+v_{rs})$;2.若$l_{1}+r_{2}+v_{ls}+v_{rs}le ans$,转移到$(l_{2}+v_{rs},r_{1}+v_{ls})$

可以利用单调性来优化,即找到满足$l_{2}le ans-r_{1}-v_{ls}-v_{rs}$且$r_{2}$最小的位置(第一种转移),那么每一组$(l_{1},r_{1})$最多转移到$k$中的两个状态(反之同理),即有$|state[k]|le 2min(|state[ls]|,|state[rs]|)$,空间复杂度为$o(nlog_{2}n)$,时间复杂度即为空间复杂度乘上二分,为$o(nlog^{ 2}_{2}n)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define ll long long
 5 #define fi first
 6 #define se second
 7 vector<pair<ll,ll> >v,f[N];
 8 int n,x,ls[N],rs[N],w[N];
 9 ll ans;
10 void dfs(int k){
11     f[k].clear();
12     if ((!ls[k])&&(!rs[k])){
13         f[k].push_back(make_pair(0,0));
14         return;
15     }
16     dfs(ls[k]);
17     dfs(rs[k]);
18     ll s=ans-w[ls[k]]-w[rs[k]];
19     for(int i=0,j=0;i<f[ls[k]].size();i++){
20         while ((j<f[rs[k]].size())&&(f[rs[k]][j].fi<=s-f[ls[k]][i].se))j++;
21         if ((j)&&(f[rs[k]][j-1].fi<=s-f[ls[k]][i].se))f[k].push_back(make_pair(f[ls[k]][i].fi+w[ls[k]],f[rs[k]][j-1].se+w[rs[k]]));
22     }
23     swap(ls[k],rs[k]);
24     for(int i=0,j=0;i<f[ls[k]].size();i++){
25         while ((j<f[rs[k]].size())&&(f[rs[k]][j].fi<=s-f[ls[k]][i].se))j++;
26         if ((j)&&(f[rs[k]][j-1].fi<=s-f[ls[k]][i].se))f[k].push_back(make_pair(f[ls[k]][i].fi+w[ls[k]],f[rs[k]][j-1].se+w[rs[k]]));
27     }
28     if (!f[k].size())return;
29     sort(f[k].begin(),f[k].end());
30     v.clear();
31     v.push_back(f[k][0]);
32     for(int i=1;i<f[k].size();i++)
33         if (f[k][i].se<v[(int)v.size()-1].se)v.push_back(f[k][i]);
34     f[k]=v;
35 }
36 bool pd(ll k){
37     ans=k;
38     dfs(1);
39     return f[1].size();
40 }
41 int main(){
42     scanf("%d",&n);
43     for(int i=2;i<=n;i++){
44         scanf("%d%d",&x,&w[i]);
45         if (!ls[x])ls[x]=i;
46         else rs[x]=i;
47     }
48     ll l=0,r=1LL*N*N;
49     while (l<r){
50         ll mid=(l+r>>1);
51         if (pd(mid))r=mid;
52         else l=mid+1;
53     }
54     printf("%lld",l);
55 } 
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/13820763.html