Author: Maksym Bevza
Tester: Arjun Arul
Editorialist: Misha Chorniy
Difficulty:
Simple
Pre-Requisites:
none
Problem Statement
You are given tree consisting N nodes. Each node i has value P_{i} assigned to it. All values P_{i} are distinct. For each node j find maximal value of node in the graph if to remove nodes adjacent to j and j.
Explanation
Subtask 1 and 2
N is less or equal than 10000. For each node will mark all nodes which are adjacent to it as dead(not usable), will find maximum value between non-marked nodes.
for i= 1..N
//Step 1
for j=1..N
alive[j] = 1; //initialize all marks as ones
//Step 2
alive[i] = 0; //mark itself as dead
for v is adjacent to i //mark all adjacent nodes
alive[v] = 0; //mark neighbours as dead
ans[i] = 0;
//Step 3
for j=1..N
if alive[j]==1 //if node j is alive
ans[i]= max(ans[i],p[j]); //find maximum value
How long it works? Obviously, the complexity of first and third step is O(N), how to find the complexity of the second step? Denote D(v) - degree of node v, for vertex i complexity of step 2 is equal to D(i), over all iterations complexity of second step is D(1)+D(2)+…+D(N), what is 2*(N-1) for trees, we see that each edge using exactly two times.
Total complexity of this algorithm is O(N*N+2*(N-2)+N*N) = O(N^2)
First and third steps are slowest in the code above, how to optimize it? We can rewrite that code a bit.
for i=1..N
alive[i]=1;
for i=1..N
alive[i]=0;
for v is adjacent to i
alive[v] = 0;
ans[i]=0;
for j=1..N
if alive[j] == 1 //if node j is non-marked
ans[i] = max(ans[i],p[j]); //update answer
alive[i]=1;
for v is adjacent to i
alive[v] = 1;
Now total complexity is O(N+2*(N-2)+N*N+2*(N-2)) = O(N^2), still O(N^2), we can make some observations, basically we have set, where the following operations can be performed:
Subtask 3
Let’s use some data structure, namely in our case, the data structure which can add/erase elements, and find the maximal value in the set of these elements, but does this thing in time less than O(N).
for i=1..N
add(P[i]);
for i=1..N
erase(P[i]);
for v is adjacent to i
erase(P[v]);
ans[i] = getMaximalValue();
add(P[i]);
for v is adjacent to i
add(P[v]);
What is the best data structure for these things, in most of the modern programming languages exists built-in data structures for such things, in C++ you can use set, Java has Set, Python has similar data structures. If your language doesn’t have built-in data structures, you can read about heaps (http://pages.cs.wisc.edu/~vernon/cs367/notes/11.PRIORITY-Q.html) and realize it.
Let’s write code with the map in C++, the similar code can be written in Java with Map or in Python with the dictionary.
set < int > S;
for i=1..N // Step 0
S.insert(P[i]); //Initialize set
//Adding element P[i] in the set
for i=1..N
//Step 1
S.erase(P[i]); //Erase value of node i from set
for v is adjacent to i //Iterate over neighbours of node i
S.erase(P[v]) //Erase values of neighbours of i
//Step 2
if !S.empty()
ans[i] = *S.rbegin(); //Value of greatest element
//Step 3, rollback of the step 1
S.insert(P[i]); //Insert value of node i from set
for v is adjacent to i //Iterate over neighbours of node i
S.insert(P[v]) //Insert values of neighbours of i
Complexity of adding/erasing of number in such data structures is O(log N), summary complexity of the first steps is O(N log N), the same is for third steps,
Total complexity is O(N log N + N log N + N log N + N log N) = O(N log N)
The overall time complexity of this approach is O(N log N).
Solution:
Setter’s solution can be found here
Tester’s solution can be found here
Please feel free to post comments if anything is not clear to you.