ZOJ--3937(点分治,斜率优化)
2016-04-27 02:00:42
【传送门】
题意:给出一棵带点权的树,点编号1~N,要求选一段路径,且仅能从标号大的点走到标号小的点,设路径为 p1->p2->p3 ....->pk,令 f = Sigma( i * weight of pi ) , 1<=i<=k,要求最大的 f
思路:考虑点分治,对于每个分治中心,将答案路径拆分为一条从中心向下标号递减 和 一条标号递增的路径。
对于当前一个子树,找出重心后,DFS一遍可以找出一条唯一的从重心向下的标号递减的路径,设路径为 W1
0:设 dep[i] 为 i 点的深度
1:对于 W1 上的结点 a1 , a2 , a3 ... 我们设 A[i] = Sigma( (i + 1 - j) * weight of aj ) , 1<=j<=i,其含义就是如果从重心走到 i 得到的 f 值
2:然后我们需要找到从重心向下标号递增的路径 W2,此时路径是不唯一的,对于 W2 上的结点 a1 , a2 , a3 ... 我们设 S[i] = Sigma( weight of aj ) , A[i] = Sigma( j * weight of aj ) , 1<=j<=i
假设我们取 W2 上的 i 点,以及 W1上的 j 点构成答案,那么 f = A[i] + ( dep[j] - 1) * S[i] + A[j]
3:将上式转化为:A[j] = -S[i] * ( dep[j] - 1) - A[i] + f
4:于是我们可以维护一个关于点集(dep[j] , A[j])的凸壳
5:因此我们只要再DFS一遍,沿着标号递增的路径,对于每个点 i 在凸壳中三分查找对于 i 的最优节点 j,更新答案
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <time.h> 5 #include <math.h> 6 #include <vector> 7 #include <iostream> 8 #include <algorithm> 9 using namespace std; 10 11 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 12 #define MP(a,b) make_pair(a,b) 13 #define PB push_back 14 #define X first 15 #define Y second 16 17 typedef long long ll; 18 typedef pair<ll,ll> pll; 19 const int inf = (1 << 30) - 1; 20 const ll INF = 1LL << 60; 21 const int MAXN = 200010; 22 23 int T,N,B[MAXN],sz[MAXN],szmin,top,pcnt; 24 bool vis[MAXN]; 25 vector<int> G[MAXN]; 26 ll ans,A[MAXN],S[MAXN]; 27 pll sta[MAXN],P[MAXN]; 28 29 inline void Init(){ 30 for(int i = 1; i <= N; ++i) G[i].clear(); 31 memset(vis,false,sizeof(vis)); 32 } 33 34 void Dfs_init(int p,int total,int pre,int &root){ 35 sz[p] = 1; 36 int tmax = -inf; 37 for(int i = 0; i < G[p].size(); ++i){ 38 int v = G[p][i]; 39 if(v == pre || vis[v]) continue; 40 Dfs_init(v,total,p,root); 41 sz[p] += sz[v]; 42 tmax = max(tmax,sz[v]); 43 } 44 tmax = max(tmax,total - sz[p]); 45 if(tmax < szmin){ 46 szmin = tmax; 47 root = p; 48 } 49 } 50 51 void Dfs_dep(int p,int d){ 52 P[++pcnt] = MP(d - 1,A[p]); 53 ans = max(ans,A[p]); 54 for(int i = 0; i < G[p].size(); ++i){ 55 int v = G[p][i]; 56 if(vis[v] || v > p) continue; 57 S[v] = S[p] + B[v]; 58 A[v] = A[p] + S[v]; 59 Dfs_dep(v,d + 1); 60 return; 61 } 62 } 63 64 void Build(){ 65 top = 0; 66 sta[++top] = P[1]; 67 for(int i = 2; i <= pcnt; ++i){ 68 while(top >= 2){ 69 ll cur = (P[i].Y - sta[top].Y) * (sta[top].X - sta[top - 1].X) 70 - (sta[top].Y - sta[top - 1].Y) * (P[i].X - sta[top].X); 71 if(cur >= 0) --top; 72 else break; 73 } 74 sta[++top] = P[i]; 75 } 76 } 77 78 ll Cal(int p,ll k){ 79 return k * sta[p].X + sta[p].Y; 80 } 81 82 ll Solve(ll k){ 83 int l = 1,r = top; 84 while(l < r - 1){ 85 int mid1 = getmid(l,r); 86 int mid2 = getmid(mid1,r); 87 if(mid1 == mid2) break; 88 ll res1 = Cal(mid1,k),res2 = Cal(mid2,k); 89 if(res1 > res2) r = mid2; 90 else l = mid1; 91 } 92 ll res = -INF; 93 for(int i = l; i <= r; ++i) res = max(res,Cal(i,k)); 94 return res; 95 } 96 97 void Dfs_res(int p,int d,int &root){ 98 ans = max(ans,A[p] + B[root]); 99 ans = max(ans,Solve(S[p]) + A[p]); 100 for(int i = 0; i < G[p].size(); ++i){ 101 int v = G[p][i]; 102 if(vis[v] || v < p) continue; 103 S[v] = S[p] + B[v]; 104 A[v] = A[p] + 1ll * (d + 1) * B[v]; 105 Dfs_res(v,d + 1,root); 106 } 107 } 108 109 void TreeDAC(int p,int total){ 110 int root; 111 szmin = N; 112 pcnt = 0; 113 Dfs_init(p,total,-1,root); 114 A[root] = S[root] = B[root]; 115 Dfs_dep(root,1); 116 Build(); 117 A[root] = S[root] = 0; 118 Dfs_res(root,1,root); 119 vis[root] = true; 120 for(int i = 0; i < G[root].size(); ++i){ 121 int v = G[root][i]; 122 if(vis[v]) continue; 123 TreeDAC(v,sz[v]); 124 } 125 } 126 127 int main(){ 128 scanf("%d",&T); 129 while(T--){ 130 scanf("%d",&N); 131 Init(); 132 for(int i = 1; i <= N; ++i) scanf("%d",&B[i]); 133 for(int i = 2; i <= N; ++i){ 134 int a; 135 scanf("%d",&a); 136 G[i].PB(a); 137 G[a].PB(i); 138 } 139 ans = 0; 140 TreeDAC(1,N); 141 printf("%lld\n",ans); 142 } 143 return 0; 144 }