Given a tree of n vertices, all the vertices are initially unmarked. On the first move, select any unmarked vertex and mark it. Then on each move, you choose an unmarked vertex adjacent to any marked vertex, mark it, and add the size of the connected component consisting only of unmarked vertices that contain the chosen vertex to your total score. The task is to maximize the total score.
Examples:
Input: n = 9; edges = { { 2, 3 }, { 1, 2 }, { 2, 5 }, { 2, 6 }, { 1, 4 }, { 4, 9 }, { 9, 7 }, { 9, 8 } };
Output: 36Input: n = 5, edges = {{1, 2}, {1, 3}, {2, 4}, {2, 5}}
Approach: To solve the problem follow the below idea:
The idea is to use dynamic programming approach to maximize the total score in a tree by efficiently updating subtree sizes and cumulative scores during a depth-first search (DFS). Explores each subtree, calculates the impact of marking vertices in terms of size and score, and reverts changes after processing each subtree to determine the optimal configuration for maximizing the overall score.
Step-by-step approach:
- Use DFS to calculate the size of each subtree rooted at every vertex.
- Use DFS to calculate the cumulative score for each subtree rooted at every vertex.
- At each vertex, update the score by considering the impact of marking vertices in the subtree.
- Revert changes after processing each subtree.
- Keep track of the maximum score obtained during the DFS traversal.
- Output the maximum total score obtained in the tree.
Below is the implementation of the above approach:
#include <bits/stdc++.h> using namespace std;
int n;
long long ans;
// siz array stores the size of each // subtree rooted at a vertex vector< int > siz;
// dp array stores the cumulative // score for each subtree vector< long long > dp;
// adj is the adjacency list representing the tree vector<vector< int > > adj;
// Calculate the size of the subtree rooted at vertex v int calcsize( int v, int p = -1)
{ siz[v] = 1;
for ( auto to : adj[v]) {
if (to == p)
continue ;
siz[v] += calcsize(to, v);
}
return siz[v];
} // Calculate the cumulative score for each subtree // rooted at vertex v long long cumdp( int v, int p = -1)
{ dp[v] = siz[v];
for ( auto to : adj[v]) {
if (to == p)
continue ;
dp[v] += cumdp(to, v);
}
return dp[v];
} // Perform the main DFS to maximize the total score void dfs( int v, int p = -1)
{ // Update the maximum score
ans = max(ans, dp[v]);
for ( auto to : adj[v]) {
if (to == p)
continue ;
// Update dp and siz arrays to represent the
// current subtree and its children
dp[v] -= dp[to];
dp[v] -= siz[to];
siz[v] -= siz[to];
siz[to] += siz[v];
dp[to] += siz[v];
dp[to] += dp[v];
// Recursively call DFS on the child
dfs(to, v);
// Revert the changes made to dp and siz arrays
// after the recursive call
dp[to] -= dp[v];
dp[to] -= siz[v];
siz[to] -= siz[v];
siz[v] += siz[to];
dp[v] += siz[to];
dp[v] += dp[to];
}
} // Drivers code int main()
{ int n = 9;
vector<vector< int > > edges
= { { 2, 3 }, { 1, 2 }, { 2, 5 }, { 2, 6 },
{ 1, 4 }, { 4, 9 }, { 9, 7 }, { 9, 8 } };
adj = vector<vector< int > >(n);
for ( int i = 0; i < n - 1; ++i) {
int x = edges[i][0], y = edges[i][1];
--x, --y;
adj[x].push_back(y);
adj[y].push_back(x);
}
ans = 0;
siz = vector< int >(n);
dp = vector< long long >(n);
// Calculate sizes and cumulative scores for each
// subtree
calcsize(0);
cumdp(0);
// Perform DFS to maximize the total score
dfs(0);
cout << ans << endl;
return 0;
} |
import java.util.ArrayList;
import java.util.List;
public class TreeScore {
static int n;
static long ans;
static List<List<Integer> > adj;
static List<Integer> siz;
static List<Long> dp;
// Calculate the size of the subtree rooted at vertex v
static int calcsize( int v, int p)
{
siz.set(v, 1 );
for ( int to : adj.get(v)) {
if (to == p)
continue ;
siz.set(v, siz.get(v) + calcsize(to, v));
}
return siz.get(v);
}
// Calculate the cumulative score for each subtree
// rooted at vertex v
static long cumdp( int v, int p)
{
dp.set(v, siz.get(v).longValue());
for ( int to : adj.get(v)) {
if (to == p)
continue ;
dp.set(v, dp.get(v) + cumdp(to, v));
}
return dp.get(v);
}
// Perform the main DFS to maximize the total score
static void dfs( int v, int p)
{
// Update the maximum score
ans = Math.max(ans, dp.get(v));
for ( int to : adj.get(v)) {
if (to == p)
continue ;
// Update dp and siz arrays to represent the
// current subtree and its children
dp.set(v, dp.get(v) - dp.get(to));
dp.set(v, dp.get(v) - siz.get(to));
siz.set(v, siz.get(v) - siz.get(to));
siz.set(to, siz.get(to) + siz.get(v));
dp.set(to, dp.get(to) + siz.get(v));
dp.set(to, dp.get(to) + dp.get(v));
// Recursively call DFS on the child
dfs(to, v);
// Revert the changes made to dp and siz arrays
// after the recursive call
dp.set(to, dp.get(to) - dp.get(v));
dp.set(to, dp.get(to) - siz.get(v));
siz.set(to, siz.get(to) - siz.get(v));
siz.set(v, siz.get(v) + siz.get(to));
dp.set(v, dp.get(v) + siz.get(to));
dp.set(v, dp.get(v) + dp.get(to));
}
}
// Driver code
public static void main(String[] args)
{
n = 9 ;
List<List<Integer> > edges = new ArrayList<>();
edges.add( new ArrayList<>(List.of( 2 , 3 )));
edges.add( new ArrayList<>(List.of( 1 , 2 )));
edges.add( new ArrayList<>(List.of( 2 , 5 )));
edges.add( new ArrayList<>(List.of( 2 , 6 )));
edges.add( new ArrayList<>(List.of( 1 , 4 )));
edges.add( new ArrayList<>(List.of( 4 , 9 )));
edges.add( new ArrayList<>(List.of( 9 , 7 )));
edges.add( new ArrayList<>(List.of( 9 , 8 )));
adj = new ArrayList<>();
for ( int i = 0 ; i < n; ++i) {
adj.add( new ArrayList<>());
}
for ( int i = 0 ; i < n - 1 ; ++i) {
int x = edges.get(i).get( 0 ),
y = edges.get(i).get( 1 );
--x;
--y;
adj.get(x).add(y);
adj.get(y).add(x);
}
ans = 0 ;
siz = new ArrayList<>(n);
dp = new ArrayList<>(n);
for ( int i = 0 ; i < n; ++i) {
siz.add( 0 );
dp.add(0L);
}
// Calculate sizes and cumulative scores for each
// subtree
calcsize( 0 , - 1 );
cumdp( 0 , - 1 );
// Perform DFS to maximize the total score
dfs( 0 , - 1 );
System.out.println(ans);
}
} |
# Python code for the above approach: n, ans = 0 , 0
# siz array stores the size of each # subtree rooted at a vertex siz = []
# dp array stores the cumulative # score for each subtree dp = []
# adj is the adjacency list representing the tree adj = []
# Calculate the size of the subtree rooted at vertex v def calcsize(v, p = - 1 ):
siz[v] = 1
for to in adj[v]:
if (to = = p):
continue
siz[v] + = calcsize(to, v)
return siz[v]
# Calculate the cumulative score for each subtree # rooted at vertex v def cumdp(v, p = - 1 ):
dp[v] = siz[v]
for to in adj[v]:
if (to = = p):
continue
dp[v] + = cumdp(to, v)
return dp[v]
# Perform the main DFS to maximize the total score def dfs(v, p = - 1 ):
# Update the maximum score
global ans
ans = max (ans, dp[v])
for to in adj[v]:
if (to = = p):
continue
# Update dp and siz arrays to represent the
# current subtree and its children
dp[v] - = dp[to]
dp[v] - = siz[to]
siz[v] - = siz[to]
siz[to] + = siz[v]
dp[to] + = siz[v]
dp[to] + = dp[v]
# Recursively call DFS on the child
dfs(to, v)
# Revert the changes made to dp and siz arrays
# after the recursive call
dp[to] - = dp[v]
dp[to] - = siz[v]
siz[to] - = siz[v]
siz[v] + = siz[to]
dp[v] + = siz[to]
dp[v] + = dp[to]
# Drivers code def main():
global ans, siz, dp, n, adj
n = 9
edges = [[ 2 , 3 ], [ 1 , 2 ], [ 2 , 5 ], [ 2 , 6 ], [ 1 , 4 ], [ 4 , 9 ], [ 9 , 7 ], [ 9 , 8 ]]
adj = [[] for _ in range (n)]
for i in range (n - 1 ):
x, y = edges[i][ 0 ], edges[i][ 1 ]
x - = 1
y - = 1
adj[x].append(y)
adj[y].append(x)
ans = 0
siz = [ 0 for _ in range (n)]
dp = [ 0 for _ in range (n)]
# Calculate sizes and cumulative scores for
# each subtree
calcsize( 0 )
cumdp( 0 )
# Perform DFS to maximize the total score
dfs( 0 )
print (ans)
if __name__ = = '__main__' :
main()
# This code is contributed by ragul21 |
using System;
using System.Collections.Generic;
class SubtreeScore
{ static int n;
static long ans;
// siz array stores the size of each
// subtree rooted at a vertex
static List< int > siz;
// dp array stores the cumulative
// score for each subtree
static List< long > dp;
// adj is the adjacency list representing the tree
static List<List< int >> adj;
// Calculate the size of the subtree rooted at vertex v
static int CalcSize( int v, int p = -1)
{
siz[v] = 1;
foreach ( int to in adj[v])
{
if (to == p)
continue ;
siz[v] += CalcSize(to, v);
}
return siz[v];
}
// Calculate the cumulative score for each subtree
// rooted at vertex v
static long CumulativeDP( int v, int p = -1)
{
dp[v] = siz[v];
foreach ( int to in adj[v])
{
if (to == p)
continue ;
dp[v] += CumulativeDP(to, v);
}
return dp[v];
}
// Perform the main DFS to maximize the total score
static void DFS( int v, int p = -1)
{
// Update the maximum score
ans = Math.Max(ans, dp[v]);
foreach ( int to in adj[v])
{
if (to == p)
continue ;
// Update dp and siz arrays to represent the
// current subtree and its children
dp[v] -= dp[to];
dp[v] -= siz[to];
siz[v] -= siz[to];
siz[to] += siz[v];
dp[to] += siz[v];
dp[to] += dp[v];
// Recursively call DFS on the child
DFS(to, v);
// Revert the changes made to dp and siz arrays
// after the recursive call
dp[to] -= dp[v];
dp[to] -= siz[v];
siz[to] -= siz[v];
siz[v] += siz[to];
dp[v] += siz[to];
dp[v] += dp[to];
}
}
// Drivers code
static void Main()
{
n = 9;
List<List< int >> edges = new List<List< int >>
{
new List< int > {2, 3}, new List< int > {1, 2}, new List< int > {2, 5}, new List< int > {2, 6},
new List< int > {1, 4}, new List< int > {4, 9}, new List< int > {9, 7}, new List< int > {9, 8}
};
adj = new List<List< int >>();
for ( int i = 0; i < n; i++)
{
adj.Add( new List< int >());
}
for ( int i = 0; i < n - 1; ++i)
{
int x = edges[i][0], y = edges[i][1];
--x; --y;
adj[x].Add(y);
adj[y].Add(x);
}
ans = 0;
siz = new List< int >( new int [n]);
dp = new List< long >( new long [n]);
// Calculate sizes and cumulative scores for each
// subtree
CalcSize(0);
CumulativeDP(0);
// Perform DFS to maximize the total score
DFS(0);
Console.WriteLine(ans);
}
} |
class TreeScore { constructor() {
this .n = 9;
this .ans = 0;
this .adj = Array.from({ length: this .n }, () => []);
this .siz = new Array( this .n).fill(0);
this .dp = new Array( this .n).fill(0);
}
calcsize(v, p) {
this .siz[v] = 1;
for (const to of this .adj[v]) {
if (to === p) continue ;
this .siz[v] += this .calcsize(to, v);
}
return this .siz[v];
}
cumdp(v, p) {
this .dp[v] = this .siz[v];
for (const to of this .adj[v]) {
if (to === p) continue ;
this .dp[v] += this .cumdp(to, v);
}
return this .dp[v];
}
dfs(v, p) {
this .ans = Math.max( this .ans, this .dp[v]);
for (const to of this .adj[v]) {
if (to === p) continue ;
this .dp[v] -= this .dp[to];
this .dp[v] -= this .siz[to];
this .siz[v] -= this .siz[to];
this .siz[to] += this .siz[v];
this .dp[to] += this .siz[v];
this .dp[to] += this .dp[v];
this .dfs(to, v);
this .dp[to] -= this .dp[v];
this .dp[to] -= this .siz[v];
this .siz[to] -= this .siz[v];
this .siz[v] += this .siz[to];
this .dp[v] += this .siz[to];
this .dp[v] += this .dp[to];
}
}
main() {
const edges = [
[2, 3],
[1, 2],
[2, 5],
[2, 6],
[1, 4],
[4, 9],
[9, 7],
[9, 8],
];
for (let i = 0; i < this .n - 1; ++i) {
const [x, y] = edges[i];
const adjustedX = x - 1;
const adjustedY = y - 1;
this .adj[adjustedX].push(adjustedY);
this .adj[adjustedY].push(adjustedX);
}
// Calculate sizes and cumulative scores for each subtree
this .calcsize(0, -1);
this .cumdp(0, -1);
// Perform DFS to maximize the total score
this .dfs(0, -1);
console.log( this .ans);
}
} const treeScore = new TreeScore();
treeScore.main(); |
36
Time complexity: O(n).
Auxiliary Space: O(n)