Codeforces 932F - Escape Through Leaf
Problem Link:
http://codeforces.com/contest/932/problem/F
Problem Statement:
F. Escape Through Leaf
time limit per test: 3 seconds
memory limit per test:256 megabytes
input: standard input
output: standard output
You are given a tree with n nodes (numbered from 1 to n) rooted at node 1. Also, each node has two values associated with it. The values for i-th node are ai and bi.
You can jump from a node to any node in its subtree. The cost of one jump from node x to node y is the product of ax and by. The total cost of a path formed by one or more jumps is sum of costs of individual jumps. For every node, calculate the minimum total cost to reach any leaf from that node. Pay attention, that root can never be leaf, even if it has degree 1.
Note that you cannot jump from a node to itself.
Input:
The first line of input contains an integer n (2 ≤ n ≤ 105) — the number of nodes in the tree.
The second line contains n space-separated integers a1, a2, ..., an (-105 ≤ ai ≤ 105).
The third line contains n space-separated integers b1, b2, ..., bn (-105 ≤ bi ≤ 105).
Next n - 1 lines contains two space-separated integers ui and vi (1 ≤ ui, vi ≤ n) describing edge between nodes ui and vi in the tree.
Output:
Output n space-separated integers, i-th of which denotes the minimum cost of a path from node i to reach any leaf.
Analysis:
Apparently, we should solve this problem by dynamic programming on tree, i.e. we should not calculate the minimum cost at node x until we have calculated the minimum cost for every node in the subtree rooted at node x. However, if we try to jump to every node in the subtree of node x, the program will not fit into the time limit. Therefore, we need to speed up our O(n2) dynamic programming solution into some sort of O(n logn) solution.
How can we optimize our solution? Let us observe the situation when we are about to calculate the minimum cost at node x. In that case, we must have determined the minimum cost for every node its subtree, i.e. we have a set {mincost(v): v in the subtree of x}. Consider what the candidates of the minimum cost of x are, we have such a set {ax*bv + mincost(v): v in the subtree of x} in which the minimum cost equals the minimal element. This form should be familiar to people with some knowledge in linear algebra: they are all in linear form, k*m + b. Then let k = bv and b = mincost(v) for every node in the subtree of x, we have a set of linear functions, and we want to calculate the minimum value at the point m = ax. This can be solved by Convex Hull Trick. If you haven’t heard about it, check this link out: https://www.cnblogs.com/ShakuganSky/p/8457943.html
Another detail that should be mentioned is that while keeping a convex hull for every node, we need to merge these hulls when moving to their parents, and we can optimize this procedure with the “merge small to large” trick.
Time Complexity:
O(n log2n)
AC Code:
1 #include <iostream> 2 #include <sstream> 3 #include <fstream> 4 #include <string> 5 #include <vector> 6 #include <deque> 7 #include <queue> 8 #include <stack> 9 #include <set> 10 #include <map> 11 #include <algorithm> 12 #include <functional> 13 #include <utility> 14 #include <bitset> 15 #include <cmath> 16 #include <cstdlib> 17 #include <ctime> 18 #include <cstdio> 19 #include <memory.h> 20 #include <iomanip> 21 #include <unordered_set> 22 #include <unordered_map> 23 using namespace std; 24 25 #define MP make_pair 26 #define FS first 27 #define SC second 28 #define LB lower_bound 29 #define PB push_back 30 #define lc p*2+1 31 #define rc p*2+2 32 33 typedef long long ll; 34 typedef pair<int,int> pi; 35 36 class line{ 37 public: 38 ll k,m; 39 bool operator< (const line &o) const{ 40 return k<o.k||(k==o.k&&m<o.m); 41 } 42 bool operator== (const line &o) const{ 43 return k==o.k&&m==o.m; 44 } 45 }; 46 47 const int Maxn=1e5+5; 48 const ll INF=1e18+5; 49 50 int n,u,v; 51 int a[Maxn],b[Maxn],pt[Maxn]; //pt[a] (pointer of 'a') is to store the cell in which the set of all current lines(linear functions) of the subtree of 'a' is stored. 52 vector<int> adj[Maxn]; 53 ll dp[Maxn]; 54 set<line> s[Maxn]; 55 56 bool check(line l1,line l2,line l3){ //determine if l2 can be deleted from the set of lines 57 double d=1.0*(l3.k-l1.k)*(l2.m-l1.m)-1.0*(l2.k-l1.k)*(l3.m-l1.m); //determine if the intersection of l3 and l1 lies left to the intersection of l2 and l1 58 if(d>=0) return true; //if so, l2 can be deleted 59 return false; 60 } 61 62 void insert(int id,line o){//insert line o into the set with an id 63 if(s[id].size()<2){ 64 s[id].insert(o); 65 return; 66 } 67 set<line>::iterator r=s[id].LB(o),l=r,t; 68 if(r!=s[id].end()&&*r==o) return; //determine if line o already exists in the set 69 if(l!=s[id].begin()){ 70 l--; 71 if(r!=s[id].end()&&check(*l,o,*r)) return; //determine if line o should be inserted 72 s[id].insert(o); 73 while(l!=s[id].begin()){ 74 t=l--; 75 if(check(*l,*t,o)){ //delete extra lines with slope less than line o 76 s[id].erase(t); 77 } 78 else break; 79 } 80 } 81 if(r!=s[id].end()){ 82 t=r++; 83 while(r!=s[id].end()){ 84 if(check(o,*t,*r)){ //delete extra lines with slope greater than line o 85 s[id].erase(t); 86 t=r++; 87 } 88 else break; 89 } 90 } 91 } 92 93 ll gety(line o,int x){ //get the value y=f(x) for linear function o 94 return o.m+(ll)x*o.k; 95 } 96 97 ll calc(int id,int x){ 98 int lb=-Maxn,ub=Maxn+1,mi; //perform a binary search on slope 99 set<line>::iterator it; 100 while(ub-lb>1){ 101 mi=(ub+lb)>>1; 102 it=s[id].LB(line{mi,INF}); 103 if(it!=s[id].begin()&&gety(*prev(it),x)<gety(*it,x)) ub=mi; //determine the direction of binary search 104 else lb=mi; 105 } 106 it=s[id].LB(line{lb,INF}); //get the line where we can get the least value at pos=x 107 return gety(*it,x); 108 } 109 110 void merge(int id1,int id2){ 111 for(auto v:s[id1]){ 112 insert(id2,v); 113 } 114 } 115 116 void dfs(int now,int pre){ 117 int weight=0,big=now; //find a heavy children and merge all the light children's line set to the heavy children's line set 118 for(auto nxt:adj[now]){ 119 if(nxt!=pre){ 120 dfs(nxt,now); 121 if(s[pt[nxt]].size()>weight){ 122 weight=s[pt[nxt]].size(); 123 big=nxt; 124 } 125 } 126 } 127 pt[now]=pt[big];//set pointer of current node to the pointer of the heavy children, since we are going to merge the other light children to the set 128 if(big==now){//the node is a leaf; initialize the leaf 129 dp[now]=0; 130 pt[now]=now; 131 insert(now,line{-Maxn,INF}); 132 insert(now,line{Maxn,INF}); 133 insert(now,line{b[now],0}); 134 return; 135 } 136 for(auto nxt:adj[now]){ 137 if(nxt!=pre&&nxt!=big){ 138 merge(pt[nxt],pt[big]); //merge 139 } 140 } 141 dp[now]=calc(pt[now],a[now]); //find the minimum value of all functions of subtrees at pos=a[now] 142 insert(pt[now],line{b[now],dp[now]}); //insert the new line into the set 143 } 144 145 int main(){ 146 scanf("%d",&n); 147 for(int i=1;i<=n;i++)scanf("%d",a+i); 148 for(int i=1;i<=n;i++)scanf("%d",b+i); 149 for(int i=1;i<n;i++){ 150 scanf("%d%d",&u,&v); 151 adj[u].PB(v); 152 adj[v].PB(u); 153 } 154 dfs(1,1); 155 for(int i=1;i<=n;i++)printf("%I64d ",dp[i]);printf("\n"); 156 return 0; 157 }