Elliot the Hawk has a very important task − the breeding season has come. He needs to prepare a nest in which his mating partner Eleonora will lay eggs, then look after her until they switch responsibility for taking care of the eggs.
There are N birds' nests standing in a line, one after another. Every two nests are positioned at distinct heights.
Elliot needs to pick a nest in which Eleonora will lay her eggs. Then, each day, Elliot will hunt from one of the other nests. At the end of the day, he will bring the food to Eleonora and move to some other nest on the opposite side of their own nest. He can hunt from each nest at most once. Moreover, on each day he needs to hunt from a nest that is positioned higher than the previous nest, and higher than their own nest.
Elliot can return to their own nest and switch roles with Eleonora at any time. In particular, he may simply prepare their nest, then refrain from hunting at all.
For example, assume the nests are positioned at heights 4 6 2 1 5. Elliot can, for instance, prepare the nest at height 1, then hunt from the nest at height 4, on the next day hunt from the nest at height 5, and on the last day hunt from the nest at height 6:
Note that in this situation, Elliot cannot hunt from the nest at height 6 just after hunting from the nest at height 4, because these two nests are on the same side of his own nest. Also, after hunting from the rightmost nest, he cannot hunt from the leftmost nest, as it is lower than the previous one. If Elliot chooses the nest at height 6 as their own nest, then he cannot hunt at all, because all other nests are placed lower.
Elliot wonders how many possible ways there are for him to choose their nest and then hunt until he changes places with Eleonora. As the result may be very large, count the number of possibilities modulo 109 + 7 (1,000,000,007).
Write a function:
class Solution { public int solution(int[] H); }
that, given a sequence of heights of nests, returns the remainder from the division by 1,000,000,007 of the number of possible ways for Elliot to choose the nest and hunt.
For example, given:
H = [ 13, 2, 5 ]the function should return 7. All the possible ways for Elliot to choose the nest and hunt are as follows, listed one per line. The first number in each line denotes the height of Elliot's own nest, and each consecutive number describes the height of a nest he will hunt from.
13 5 13 5 2 5 13 2 5 2 13 2On the other hand, for the following array:
H = [ 4, 6, 2, 1, 5 ]the function should return 23. One of the possible ways Elliot can act is depicted in the figure above.
Write an efficient algorithm for the following assumptions:
- N is an integer within the range [1..50,000];
- each element of array H is an integer within the range [1..1,000,000,000];
- the elements of H are all distinct.
using System;
// you can also use other imports, for example:
// using System.Collections.Generic;
// you can write to stdout for debugging purposes, e.g.
// Console.WriteLine("this is a debug message");
class DPNode
{
// total number of combinations starting from this node
public ulong Total { get; set; }
// number of combinations that start from this node and end on the right of this node
public ulong RightStart { get; set; }
// number of combinations that start from this node and end on the left of this node
public ulong LeftStart { get; set; }
public long Value { get; set; }
}
class DPLazyNode
{
// update values
// number of LeftStart that needs to be added to LeftStart value
public ulong LL { get; set; }
// number of RightStart that needs to be added to LeftStart value
public ulong RL { get; set; }
// number of LeftStart that needs to be added to RightStart value
public ulong LR { get; set; }
// number of RightStart that needs to be added to RightStart value
public ulong RR { get; set; }
}
class LazySegmentTree
{
DPNode[] tree; // To store segment tree
DPLazyNode[] lazy; // To store pending updates
private const int PROCESS_LEFT = 0; // process LeftStart mode
private const int PROCESS_RIGHT = 1; // Process RightStart mode
private const int PROCESS_TOTAL = 2; // Process Total mode
private const ulong ModuloValue = 1000000007;
private ulong GetModuloValue(ulong u)
{
return u % ModuloValue;
}
private void ProcessRangeUpdate(int nodeIndex, int currentStart, int currentEnd, int queryStart,
int queryEnd, int processMode, ulong totalValue)
{
if (lazy[nodeIndex].LR != 0 || lazy[nodeIndex].LL != 0 || lazy[nodeIndex].RL != 0 || lazy[nodeIndex].RR != 0)
{
//// update tree[si] according to RR,LR,RL,LL
var updateRight = GetModuloValue(lazy[nodeIndex].RR * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LR * tree[nodeIndex].LeftStart);
var updateLeft = GetModuloValue(lazy[nodeIndex].RL * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LL * tree[nodeIndex].LeftStart);
tree[nodeIndex].LeftStart += updateLeft;
tree[nodeIndex].RightStart += updateRight;
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
// leaf node check
if (currentStart != currentEnd)
{
// Propagate Values to lazy children...
AddToLazyNode(lazy[nodeIndex * 2 + 1], lazy[nodeIndex], nodeIndex * 2 + 1);
AddToLazyNode(lazy[nodeIndex * 2 + 2], lazy[nodeIndex], nodeIndex * 2 + 2);
}
// because the update is made, set lazy values from this node to 0
lazy[nodeIndex].RR = 0;
lazy[nodeIndex].RL = 0;
lazy[nodeIndex].LL = 0;
lazy[nodeIndex].LR = 0;
}
// out of range
if (currentStart > currentEnd || currentStart > queryEnd || currentEnd < queryStart)
return;
// Current segment is fully in range
if (currentStart >= queryStart && currentEnd <= queryEnd)
{
// Update current node according to process mode
if (processMode == PROCESS_LEFT)
{
tree[nodeIndex].LeftStart += tree[nodeIndex].RightStart;
// we are only interested in the modulo value
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
}
else if (processMode == PROCESS_RIGHT)
{
tree[nodeIndex].RightStart += tree[nodeIndex].LeftStart;
// we are only interested in the modulo value
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
}
else if (processMode == PROCESS_TOTAL)
{
tree[nodeIndex].Total += totalValue;
tree[nodeIndex].LeftStart += 1;
tree[nodeIndex].RightStart += 1;
tree[nodeIndex].Total = GetModuloValue(tree[nodeIndex].Total);
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
}
// leaf node check
if (currentStart != currentEnd)
{
// When Update R: LR += 1 + LL; RR += RL
// When Update L: RL += 1 + RR; LL += LR;
if (processMode == PROCESS_LEFT)
{
if (tree[nodeIndex * 2 + 1].Total != 0)
{
lazy[nodeIndex * 2 + 1].RL += 1 + lazy[nodeIndex * 2 + 1].RR;
lazy[nodeIndex * 2 + 1].LL += lazy[nodeIndex * 2 + 1].LR;
lazy[nodeIndex * 2 + 1].RL = GetModuloValue(lazy[nodeIndex * 2 + 1].RL);
lazy[nodeIndex * 2 + 1].LL = GetModuloValue(lazy[nodeIndex * 2 + 1].LL);
}
if (tree[nodeIndex * 2 + 2].Total != 0)
{
lazy[nodeIndex * 2 + 2].RL += 1 + lazy[nodeIndex * 2 + 2].RR;
lazy[nodeIndex * 2 + 2].LL += lazy[nodeIndex * 2 + 2].LR;
lazy[nodeIndex * 2 + 2].RL = GetModuloValue(lazy[nodeIndex * 2 + 2].RL);
lazy[nodeIndex * 2 + 2].LL = GetModuloValue(lazy[nodeIndex * 2 + 2].LL);
}
}
else if (processMode == PROCESS_RIGHT)
{
if (tree[nodeIndex * 2 + 1].Total != 0)
{
lazy[nodeIndex * 2 + 1].LR += 1 + lazy[nodeIndex * 2 + 1].LL;
lazy[nodeIndex * 2 + 1].RR += lazy[nodeIndex * 2 + 1].RL;
lazy[nodeIndex * 2 + 1].LR = GetModuloValue(lazy[nodeIndex * 2 + 1].LR);
lazy[nodeIndex * 2 + 1].RR = GetModuloValue(lazy[nodeIndex * 2 + 1].RR);
}
if (tree[nodeIndex * 2 + 2].Total != 0)
{
lazy[nodeIndex * 2 + 2].LR += 1 + lazy[nodeIndex * 2 + 2].LL;
lazy[nodeIndex * 2 + 2].RR += lazy[nodeIndex * 2 + 2].RL;
lazy[nodeIndex * 2 + 2].LR = GetModuloValue(lazy[nodeIndex * 2 + 2].LR);
lazy[nodeIndex * 2 + 2].RR = GetModuloValue(lazy[nodeIndex * 2 + 2].RR);
}
}
}
// because the processing segment is fully in range - return
return;
}
// not completly in range (but overlaps) process children
int mid = (currentStart + currentEnd) / 2;
ProcessRangeUpdate(nodeIndex * 2 + 1, currentStart, mid, queryStart, queryEnd, processMode, totalValue);
ProcessRangeUpdate(nodeIndex * 2 + 2, mid + 1, currentEnd, queryStart, queryEnd, processMode, totalValue);
//Update this node with the results of the children
tree[nodeIndex] = MergeNodes(tree[nodeIndex * 2 + 1], tree[nodeIndex * 2 + 2]);
}
public void UpdateRange(int arrayLenth, int queryStart, int queryEnd, int processMode, ulong totalValue = 0)
{
ProcessRangeUpdate(0, 0, arrayLenth - 1, queryStart, queryEnd, processMode, totalValue);
}
// returns sum in range
private ulong ProcessSumInRange(int currentStart, int currentEnd, int queryStart, int queryEnd, int nodeIndex, int processMode)
{
if (lazy[nodeIndex].LR != 0 || lazy[nodeIndex].LL != 0 || lazy[nodeIndex].RL != 0 || lazy[nodeIndex].RR != 0)
{
//// update tree[si] according to RR,LR,RL,LL
var updateRight = GetModuloValue(lazy[nodeIndex].RR * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LR * tree[nodeIndex].LeftStart);
var updateLeft = GetModuloValue(lazy[nodeIndex].RL * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LL * tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart += updateRight;
tree[nodeIndex].LeftStart += updateLeft;
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart % ModuloValue);
// leaf node check
if (currentStart != currentEnd)
{
// propagate lazy data to child nodes
AddToLazyNode(lazy[nodeIndex * 2 + 1], lazy[nodeIndex], nodeIndex * 2 + 1);
AddToLazyNode(lazy[nodeIndex * 2 + 2], lazy[nodeIndex], nodeIndex * 2 + 2);
}
// lazy data is processed, set values to 0
lazy[nodeIndex].RR = 0;
lazy[nodeIndex].RL = 0;
lazy[nodeIndex].LL = 0;
lazy[nodeIndex].LR = 0;
}
// return if out of range
if (currentStart > currentEnd || currentStart > queryEnd || currentEnd < queryStart)
return 0;
// this segment completely lies in range
if (currentStart >= queryStart && currentEnd <= queryEnd)
{
if (processMode == PROCESS_LEFT)
{
return tree[nodeIndex].LeftStart;
}
else if (processMode == PROCESS_RIGHT)
{
return tree[nodeIndex].RightStart;
}
else if (processMode == PROCESS_TOTAL)
{
return tree[nodeIndex].Total;
}
}
// part of this segment overlaps with query range
int mid = (currentStart + currentEnd) / 2;
return GetModuloValue(ProcessSumInRange(currentStart, mid, queryStart, queryEnd, 2 * nodeIndex + 1, processMode) +
ProcessSumInRange(mid + 1, currentEnd, queryStart, queryEnd, 2 * nodeIndex + 2, processMode));
}
// get sum of elements in range
public ulong GetSumInRange(int arrayLength, int queryStart, int queryEnd, int processMode)
{
// Check for erroneous input values
if (queryStart < 0 || queryEnd > arrayLength - 1 || queryStart > queryEnd)
{
Console.WriteLine("GetSum - Invalid Input");
return 0;
}
return ProcessSumInRange(0, arrayLength - 1, queryStart, queryEnd, 0, processMode);
}
private void BuildSegmentTreePart(int[] arr, int currentStart, int currentEnd, int nodeIndex)
{
// return if out of range
if (currentStart > currentEnd)
return;
// only one element in current segment, create leaf node
if (currentStart == currentEnd)
{
tree[nodeIndex] = CreateNewNode(currentStart);
lazy[nodeIndex] = CreateNewLazyNode(currentStart);
return;
}
// there are more than one element
int mid = (currentStart + currentEnd) / 2;
BuildSegmentTreePart(arr, currentStart, mid, nodeIndex * 2 + 1);
BuildSegmentTreePart(arr, mid + 1, currentEnd, nodeIndex * 2 + 2);
tree[nodeIndex] = MergeNodes(tree[nodeIndex * 2 + 1], tree[nodeIndex * 2 + 2]);
lazy[nodeIndex] = CreateNewLazyNode();
}
/* Function to construct segment tree from given array.
This function allocates memory for segment tree and
calls constructSTUtil() to fill the allocated memory */
public void BuildSegmentTree(int[] arr)
{
int arrayLength = arr.Length;
int n = (int)(Math.Ceiling(Math.Log(arrayLength) / Math.Log(2)));
//Maximum size of segment tree
int maxTreeSize = 2 * (int)Math.Pow(2, n) - 1;
tree = new DPNode[maxTreeSize];
lazy = new DPLazyNode[maxTreeSize];
// Fill the allocated memory st
BuildSegmentTreePart(arr, 0, arrayLength - 1, 0);
}
private DPNode CreateNewNode(int i)
{
return new DPNode
{
Total = 0,
LeftStart = 0,
RightStart = 0,
Value = i
};
}
// The function to update a value in input array and segment tree.
// It uses updateValueUtil() to update the value in segment tree
public void ProcessArrayElement(int arrayLength, int arrayIndex)
{
// Check for erroneous input index
if (arrayIndex < 0 || arrayIndex > arrayLength - 1)
{
Console.WriteLine("Input is out of range");
return;
}
var sumLeft = arrayIndex > 0 ? GetSumInRange(arrayLength, 0, arrayIndex - 1, 0) : 0;
var sumRight = arrayIndex < arrayLength - 1 ? GetSumInRange(arrayLength, arrayIndex + 1, arrayLength - 1, 1) : 0;
var total = GetModuloValue(1 + sumLeft + sumRight);
// update current node/nest combinations
UpdateRange(arrayLength, arrayIndex, arrayIndex, 2, total);
// update nodes on the left
UpdateRange(arrayLength, 0, arrayIndex - 1, 1);
// update nodes on the right
UpdateRange(arrayLength, arrayIndex + 1, arrayLength - 1, 0);
}
private DPLazyNode CreateNewLazyNode(int i = -1)
{
return new DPLazyNode
{
LL = 0,
RR = 0,
LR = 0,
RL = 0
};
}
private DPNode MergeNodes(DPNode first, DPNode second)
{
return new DPNode
{
Total = GetModuloValue(first.Total + second.Total),
LeftStart = GetModuloValue(first.LeftStart + second.LeftStart),
RightStart = GetModuloValue(first.RightStart + second.RightStart),
Value = -1
};
}
private void AddToLazyNode(DPLazyNode child, DPLazyNode parent, int childIndex)
{
// this node is not yet processed to ignore the update data until then
if (tree[childIndex].Total == 0)
return;
var childRRUpdate = parent.RR + GetModuloValue(parent.RR * child.RR) + GetModuloValue(parent.LR * child.RL);
var childLRUpdate = parent.LR + GetModuloValue(parent.LR * child.LL) + GetModuloValue(child.LR * parent.RR);
var childLLUpdate = parent.LL + GetModuloValue(parent.LL * child.LL) + GetModuloValue(parent.RL * child.LR);
var childRLUpdate = parent.RL + GetModuloValue(parent.RL * child.RR) + GetModuloValue(child.RL * parent.LL);
child.RR += childRRUpdate;
child.LR += childLRUpdate;
child.LL += childLLUpdate;
child.RL += childRLUpdate;
child.RR = GetModuloValue(child.RR);
child.LR = GetModuloValue(child.LR);
child.LL = GetModuloValue(child.LL);
child.RL = GetModuloValue(child.RL);
}
}
public struct ValueIndex
{
public int Value { get; set; }
public int Index { get; set; }
}
public class MergeSort
{
public ValueIndex[] ValueIndex { get; set; }
private ValueIndex[] HelpValueIndex { get; set; }
private void CreateValueIndexLists(int[] array)
{
ValueIndex = new ValueIndex[array.Length];
HelpValueIndex = new ValueIndex[array.Length];
for (var i = 0; i < array.Length; i++)
{
ValueIndex[i] = new ValueIndex
{
Value = array[i],
Index = i
};
HelpValueIndex[i] = new ValueIndex
{
Value = array[i],
Index = i
};
}
}
public void Sort(int[] array, int n)
{
CreateValueIndexLists(array);
TopDownMergeSort(ValueIndex, HelpValueIndex, n);
}
public void TopDownMergeSort(ValueIndex[] sortArray, ValueIndex[] helpSortArray, int n)
{
CopyArray(sortArray, 0, n, helpSortArray);
TopDownSplitMerge(helpSortArray, 0, n, sortArray);
}
public void TopDownSplitMerge(ValueIndex[] helpSortArray, int startIndex, int endIndex, ValueIndex[] sortArray)
{
if (endIndex - startIndex < 2)
return;
var iMiddle = (endIndex + startIndex) / 2;
TopDownSplitMerge(sortArray, startIndex, iMiddle, helpSortArray);
TopDownSplitMerge(sortArray, iMiddle, endIndex, helpSortArray);
TopDownMerge(helpSortArray, startIndex, iMiddle, endIndex, sortArray);
}
public void TopDownMerge(ValueIndex[] sortArray, int startIndex, int middleIndex, int endIndex, ValueIndex[] helpSortArray)
{
int i = startIndex, j = middleIndex;
// While there are elements in the left or right runs...
for (var k = startIndex; k < endIndex; k++)
{
// If left run head exists and is <= existing right run head.
if (i < middleIndex && (j >= endIndex || sortArray[i].Value <= sortArray[j].Value))
{
helpSortArray[k] = sortArray[i];
i = i + 1;
}
else
{
helpSortArray[k] = sortArray[j];
j = j + 1;
}
}
}
public void CopyArray(ValueIndex[] A, int startIndex, int endIndex, ValueIndex[] B)
{
for (var k = startIndex; k < endIndex; k++)
B[k] = A[k];
}
}
class Solution {
public int solution(int[] H) {
// write your code in C# 6.0 with .NET 4.5 (Mono)
var N = H.Length;
// the array is empty there are no combinations
if (N == 0)
return 0;
if (N == 1)
return 1;
// for this solution I'm using Lazy Propagation Segment tree
// Complexity to build is O(n)
// Complexity of one query update is O(logn) so in total for our calculations we'll have complexity of O(nlogn)
// Space needed for building Segment Tree is O(k*N) where k in interval (2-3)
LazySegmentTree tree = new LazySegmentTree();
// Build segment tree from given array
tree.BuildSegmentTree(H);
// I use Merge Sort because it has time complexity of O(nlogn) and space complexity of O(n)
var mergeSort = new MergeSort();
mergeSort.Sort(H, H.Length);
// process the elements in ascending oreder
for (int i = 0; i < H.Length; i++)
{
tree.ProcessArrayElement(H.Length, mergeSort.ValueIndex[i].Index);
}
var ret = tree.GetSumInRange(N, 0, N - 1, 2);
return Convert.ToInt32(ret % 1000000007);
}
}
using System;
// you can also use other imports, for example:
// using System.Collections.Generic;
// you can write to stdout for debugging purposes, e.g.
// Console.WriteLine("this is a debug message");
class DPNode
{
// total number of combinations starting from this node
public ulong Total { get; set; }
// number of combinations that start from this node and end on the right of this node
public ulong RightStart { get; set; }
// number of combinations that start from this node and end on the left of this node
public ulong LeftStart { get; set; }
public long Value { get; set; }
}
class DPLazyNode
{
// update values
// number of LeftStart that needs to be added to LeftStart value
public ulong LL { get; set; }
// number of RightStart that needs to be added to LeftStart value
public ulong RL { get; set; }
// number of LeftStart that needs to be added to RightStart value
public ulong LR { get; set; }
// number of RightStart that needs to be added to RightStart value
public ulong RR { get; set; }
}
class LazySegmentTree
{
DPNode[] tree; // To store segment tree
DPLazyNode[] lazy; // To store pending updates
private const int PROCESS_LEFT = 0; // process LeftStart mode
private const int PROCESS_RIGHT = 1; // Process RightStart mode
private const int PROCESS_TOTAL = 2; // Process Total mode
private const ulong ModuloValue = 1000000007;
private ulong GetModuloValue(ulong u)
{
return u % ModuloValue;
}
private void ProcessRangeUpdate(int nodeIndex, int currentStart, int currentEnd, int queryStart,
int queryEnd, int processMode, ulong totalValue)
{
if (lazy[nodeIndex].LR != 0 || lazy[nodeIndex].LL != 0 || lazy[nodeIndex].RL != 0 || lazy[nodeIndex].RR != 0)
{
//// update tree[si] according to RR,LR,RL,LL
var updateRight = GetModuloValue(lazy[nodeIndex].RR * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LR * tree[nodeIndex].LeftStart);
var updateLeft = GetModuloValue(lazy[nodeIndex].RL * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LL * tree[nodeIndex].LeftStart);
tree[nodeIndex].LeftStart += updateLeft;
tree[nodeIndex].RightStart += updateRight;
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
// leaf node check
if (currentStart != currentEnd)
{
// Propagate Values to lazy children...
AddToLazyNode(lazy[nodeIndex * 2 + 1], lazy[nodeIndex], nodeIndex * 2 + 1);
AddToLazyNode(lazy[nodeIndex * 2 + 2], lazy[nodeIndex], nodeIndex * 2 + 2);
}
// because the update is made, set lazy values from this node to 0
lazy[nodeIndex].RR = 0;
lazy[nodeIndex].RL = 0;
lazy[nodeIndex].LL = 0;
lazy[nodeIndex].LR = 0;
}
// out of range
if (currentStart > currentEnd || currentStart > queryEnd || currentEnd < queryStart)
return;
// Current segment is fully in range
if (currentStart >= queryStart && currentEnd <= queryEnd)
{
// Update current node according to process mode
if (processMode == PROCESS_LEFT)
{
tree[nodeIndex].LeftStart += tree[nodeIndex].RightStart;
// we are only interested in the modulo value
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
}
else if (processMode == PROCESS_RIGHT)
{
tree[nodeIndex].RightStart += tree[nodeIndex].LeftStart;
// we are only interested in the modulo value
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
}
else if (processMode == PROCESS_TOTAL)
{
tree[nodeIndex].Total += totalValue;
tree[nodeIndex].LeftStart += 1;
tree[nodeIndex].RightStart += 1;
tree[nodeIndex].Total = GetModuloValue(tree[nodeIndex].Total);
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
}
// leaf node check
if (currentStart != currentEnd)
{
// When Update R: LR += 1 + LL; RR += RL
// When Update L: RL += 1 + RR; LL += LR;
if (processMode == PROCESS_LEFT)
{
if (tree[nodeIndex * 2 + 1].Total != 0)
{
lazy[nodeIndex * 2 + 1].RL += 1 + lazy[nodeIndex * 2 + 1].RR;
lazy[nodeIndex * 2 + 1].LL += lazy[nodeIndex * 2 + 1].LR;
lazy[nodeIndex * 2 + 1].RL = GetModuloValue(lazy[nodeIndex * 2 + 1].RL);
lazy[nodeIndex * 2 + 1].LL = GetModuloValue(lazy[nodeIndex * 2 + 1].LL);
}
if (tree[nodeIndex * 2 + 2].Total != 0)
{
lazy[nodeIndex * 2 + 2].RL += 1 + lazy[nodeIndex * 2 + 2].RR;
lazy[nodeIndex * 2 + 2].LL += lazy[nodeIndex * 2 + 2].LR;
lazy[nodeIndex * 2 + 2].RL = GetModuloValue(lazy[nodeIndex * 2 + 2].RL);
lazy[nodeIndex * 2 + 2].LL = GetModuloValue(lazy[nodeIndex * 2 + 2].LL);
}
}
else if (processMode == PROCESS_RIGHT)
{
if (tree[nodeIndex * 2 + 1].Total != 0)
{
lazy[nodeIndex * 2 + 1].LR += 1 + lazy[nodeIndex * 2 + 1].LL;
lazy[nodeIndex * 2 + 1].RR += lazy[nodeIndex * 2 + 1].RL;
lazy[nodeIndex * 2 + 1].LR = GetModuloValue(lazy[nodeIndex * 2 + 1].LR);
lazy[nodeIndex * 2 + 1].RR = GetModuloValue(lazy[nodeIndex * 2 + 1].RR);
}
if (tree[nodeIndex * 2 + 2].Total != 0)
{
lazy[nodeIndex * 2 + 2].LR += 1 + lazy[nodeIndex * 2 + 2].LL;
lazy[nodeIndex * 2 + 2].RR += lazy[nodeIndex * 2 + 2].RL;
lazy[nodeIndex * 2 + 2].LR = GetModuloValue(lazy[nodeIndex * 2 + 2].LR);
lazy[nodeIndex * 2 + 2].RR = GetModuloValue(lazy[nodeIndex * 2 + 2].RR);
}
}
}
// because the processing segment is fully in range - return
return;
}
// not completly in range (but overlaps) process children
int mid = (currentStart + currentEnd) / 2;
ProcessRangeUpdate(nodeIndex * 2 + 1, currentStart, mid, queryStart, queryEnd, processMode, totalValue);
ProcessRangeUpdate(nodeIndex * 2 + 2, mid + 1, currentEnd, queryStart, queryEnd, processMode, totalValue);
//Update this node with the results of the children
tree[nodeIndex] = MergeNodes(tree[nodeIndex * 2 + 1], tree[nodeIndex * 2 + 2]);
}
public void UpdateRange(int arrayLenth, int queryStart, int queryEnd, int processMode, ulong totalValue = 0)
{
ProcessRangeUpdate(0, 0, arrayLenth - 1, queryStart, queryEnd, processMode, totalValue);
}
// returns sum in range
private ulong ProcessSumInRange(int currentStart, int currentEnd, int queryStart, int queryEnd, int nodeIndex, int processMode)
{
if (lazy[nodeIndex].LR != 0 || lazy[nodeIndex].LL != 0 || lazy[nodeIndex].RL != 0 || lazy[nodeIndex].RR != 0)
{
//// update tree[si] according to RR,LR,RL,LL
var updateRight = GetModuloValue(lazy[nodeIndex].RR * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LR * tree[nodeIndex].LeftStart);
var updateLeft = GetModuloValue(lazy[nodeIndex].RL * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LL * tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart += updateRight;
tree[nodeIndex].LeftStart += updateLeft;
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart % ModuloValue);
// leaf node check
if (currentStart != currentEnd)
{
// propagate lazy data to child nodes
AddToLazyNode(lazy[nodeIndex * 2 + 1], lazy[nodeIndex], nodeIndex * 2 + 1);
AddToLazyNode(lazy[nodeIndex * 2 + 2], lazy[nodeIndex], nodeIndex * 2 + 2);
}
// lazy data is processed, set values to 0
lazy[nodeIndex].RR = 0;
lazy[nodeIndex].RL = 0;
lazy[nodeIndex].LL = 0;
lazy[nodeIndex].LR = 0;
}
// return if out of range
if (currentStart > currentEnd || currentStart > queryEnd || currentEnd < queryStart)
return 0;
// this segment completely lies in range
if (currentStart >= queryStart && currentEnd <= queryEnd)
{
if (processMode == PROCESS_LEFT)
{
return tree[nodeIndex].LeftStart;
}
else if (processMode == PROCESS_RIGHT)
{
return tree[nodeIndex].RightStart;
}
else if (processMode == PROCESS_TOTAL)
{
return tree[nodeIndex].Total;
}
}
// part of this segment overlaps with query range
int mid = (currentStart + currentEnd) / 2;
return GetModuloValue(ProcessSumInRange(currentStart, mid, queryStart, queryEnd, 2 * nodeIndex + 1, processMode) +
ProcessSumInRange(mid + 1, currentEnd, queryStart, queryEnd, 2 * nodeIndex + 2, processMode));
}
// get sum of elements in range
public ulong GetSumInRange(int arrayLength, int queryStart, int queryEnd, int processMode)
{
// Check for erroneous input values
if (queryStart < 0 || queryEnd > arrayLength - 1 || queryStart > queryEnd)
{
Console.WriteLine("GetSum - Invalid Input");
return 0;
}
return ProcessSumInRange(0, arrayLength - 1, queryStart, queryEnd, 0, processMode);
}
private void BuildSegmentTreePart(int[] arr, int currentStart, int currentEnd, int nodeIndex)
{
// return if out of range
if (currentStart > currentEnd)
return;
// only one element in current segment, create leaf node
if (currentStart == currentEnd)
{
tree[nodeIndex] = CreateNewNode(currentStart);
lazy[nodeIndex] = CreateNewLazyNode(currentStart);
return;
}
// there are more than one element
int mid = (currentStart + currentEnd) / 2;
BuildSegmentTreePart(arr, currentStart, mid, nodeIndex * 2 + 1);
BuildSegmentTreePart(arr, mid + 1, currentEnd, nodeIndex * 2 + 2);
tree[nodeIndex] = MergeNodes(tree[nodeIndex * 2 + 1], tree[nodeIndex * 2 + 2]);
lazy[nodeIndex] = CreateNewLazyNode();
}
/* Function to construct segment tree from given array.
This function allocates memory for segment tree and
calls constructSTUtil() to fill the allocated memory */
public void BuildSegmentTree(int[] arr)
{
int arrayLength = arr.Length;
int n = (int)(Math.Ceiling(Math.Log(arrayLength) / Math.Log(2)));
//Maximum size of segment tree
int maxTreeSize = 2 * (int)Math.Pow(2, n) - 1;
tree = new DPNode[maxTreeSize];
lazy = new DPLazyNode[maxTreeSize];
// Fill the allocated memory st
BuildSegmentTreePart(arr, 0, arrayLength - 1, 0);
}
private DPNode CreateNewNode(int i)
{
return new DPNode
{
Total = 0,
LeftStart = 0,
RightStart = 0,
Value = i
};
}
// The function to update a value in input array and segment tree.
// It uses updateValueUtil() to update the value in segment tree
public void ProcessArrayElement(int arrayLength, int arrayIndex)
{
// Check for erroneous input index
if (arrayIndex < 0 || arrayIndex > arrayLength - 1)
{
Console.WriteLine("Input is out of range");
return;
}
var sumLeft = arrayIndex > 0 ? GetSumInRange(arrayLength, 0, arrayIndex - 1, 0) : 0;
var sumRight = arrayIndex < arrayLength - 1 ? GetSumInRange(arrayLength, arrayIndex + 1, arrayLength - 1, 1) : 0;
var total = GetModuloValue(1 + sumLeft + sumRight);
// update current node/nest combinations
UpdateRange(arrayLength, arrayIndex, arrayIndex, 2, total);
// update nodes on the left
UpdateRange(arrayLength, 0, arrayIndex - 1, 1);
// update nodes on the right
UpdateRange(arrayLength, arrayIndex + 1, arrayLength - 1, 0);
}
private DPLazyNode CreateNewLazyNode(int i = -1)
{
return new DPLazyNode
{
LL = 0,
RR = 0,
LR = 0,
RL = 0
};
}
private DPNode MergeNodes(DPNode first, DPNode second)
{
return new DPNode
{
Total = GetModuloValue(first.Total + second.Total),
LeftStart = GetModuloValue(first.LeftStart + second.LeftStart),
RightStart = GetModuloValue(first.RightStart + second.RightStart),
Value = -1
};
}
private void AddToLazyNode(DPLazyNode child, DPLazyNode parent, int childIndex)
{
// this node is not yet processed to ignore the update data until then
if (tree[childIndex].Total == 0)
return;
var childRRUpdate = parent.RR + GetModuloValue(parent.RR * child.RR) + GetModuloValue(parent.LR * child.RL);
var childLRUpdate = parent.LR + GetModuloValue(parent.LR * child.LL) + GetModuloValue(child.LR * parent.RR);
var childLLUpdate = parent.LL + GetModuloValue(parent.LL * child.LL) + GetModuloValue(parent.RL * child.LR);
var childRLUpdate = parent.RL + GetModuloValue(parent.RL * child.RR) + GetModuloValue(child.RL * parent.LL);
child.RR += childRRUpdate;
child.LR += childLRUpdate;
child.LL += childLLUpdate;
child.RL += childRLUpdate;
child.RR = GetModuloValue(child.RR);
child.LR = GetModuloValue(child.LR);
child.LL = GetModuloValue(child.LL);
child.RL = GetModuloValue(child.RL);
}
}
public struct ValueIndex
{
public int Value { get; set; }
public int Index { get; set; }
}
public class MergeSort
{
public ValueIndex[] ValueIndex { get; set; }
private ValueIndex[] HelpValueIndex { get; set; }
private void CreateValueIndexLists(int[] array)
{
ValueIndex = new ValueIndex[array.Length];
HelpValueIndex = new ValueIndex[array.Length];
for (var i = 0; i < array.Length; i++)
{
ValueIndex[i] = new ValueIndex
{
Value = array[i],
Index = i
};
HelpValueIndex[i] = new ValueIndex
{
Value = array[i],
Index = i
};
}
}
public void Sort(int[] array, int n)
{
CreateValueIndexLists(array);
TopDownMergeSort(ValueIndex, HelpValueIndex, n);
}
public void TopDownMergeSort(ValueIndex[] sortArray, ValueIndex[] helpSortArray, int n)
{
CopyArray(sortArray, 0, n, helpSortArray);
TopDownSplitMerge(helpSortArray, 0, n, sortArray);
}
public void TopDownSplitMerge(ValueIndex[] helpSortArray, int startIndex, int endIndex, ValueIndex[] sortArray)
{
if (endIndex - startIndex < 2)
return;
var iMiddle = (endIndex + startIndex) / 2;
TopDownSplitMerge(sortArray, startIndex, iMiddle, helpSortArray);
TopDownSplitMerge(sortArray, iMiddle, endIndex, helpSortArray);
TopDownMerge(helpSortArray, startIndex, iMiddle, endIndex, sortArray);
}
public void TopDownMerge(ValueIndex[] sortArray, int startIndex, int middleIndex, int endIndex, ValueIndex[] helpSortArray)
{
int i = startIndex, j = middleIndex;
// While there are elements in the left or right runs...
for (var k = startIndex; k < endIndex; k++)
{
// If left run head exists and is <= existing right run head.
if (i < middleIndex && (j >= endIndex || sortArray[i].Value <= sortArray[j].Value))
{
helpSortArray[k] = sortArray[i];
i = i + 1;
}
else
{
helpSortArray[k] = sortArray[j];
j = j + 1;
}
}
}
public void CopyArray(ValueIndex[] A, int startIndex, int endIndex, ValueIndex[] B)
{
for (var k = startIndex; k < endIndex; k++)
B[k] = A[k];
}
}
class Solution {
public int solution(int[] H) {
// write your code in C# 6.0 with .NET 4.5 (Mono)
var N = H.Length;
// the array is empty there are no combinations
if (N == 0)
return 0;
if (N == 1)
return 1;
// for this solution I'm using Lazy Propagation Segment tree
// Complexity to build is O(n)
// Complexity of one query update is O(logn) so in total for our calculations we'll have complexity of O(nlogn)
// Space needed for building Segment Tree is O(k*N) where k in interval (2-3)
LazySegmentTree tree = new LazySegmentTree();
// Build segment tree from given array
tree.BuildSegmentTree(H);
// I use Merge Sort because it has time complexity of O(nlogn) and space complexity of O(n)
var mergeSort = new MergeSort();
mergeSort.Sort(H, H.Length);
// process the elements in ascending oreder
for (int i = 0; i < H.Length; i++)
{
tree.ProcessArrayElement(H.Length, mergeSort.ValueIndex[i].Index);
}
var ret = tree.GetSumInRange(N, 0, N - 1, 2);
return Convert.ToInt32(ret % 1000000007);
}
}
using System;
// you can also use other imports, for example:
// using System.Collections.Generic;
// you can write to stdout for debugging purposes, e.g.
// Console.WriteLine("this is a debug message");
class DPNode
{
// total number of combinations starting from this node
public ulong Total { get; set; }
// number of combinations that start from this node and end on the right of this node
public ulong RightStart { get; set; }
// number of combinations that start from this node and end on the left of this node
public ulong LeftStart { get; set; }
public long Value { get; set; }
}
class DPLazyNode
{
// update values
// number of LeftStart that needs to be added to LeftStart value
public ulong LL { get; set; }
// number of RightStart that needs to be added to LeftStart value
public ulong RL { get; set; }
// number of LeftStart that needs to be added to RightStart value
public ulong LR { get; set; }
// number of RightStart that needs to be added to RightStart value
public ulong RR { get; set; }
}
class LazySegmentTree
{
DPNode[] tree; // To store segment tree
DPLazyNode[] lazy; // To store pending updates
private const int PROCESS_LEFT = 0; // process LeftStart mode
private const int PROCESS_RIGHT = 1; // Process RightStart mode
private const int PROCESS_TOTAL = 2; // Process Total mode
private const ulong ModuloValue = 1000000007;
private ulong GetModuloValue(ulong u)
{
return u % ModuloValue;
}
private void ProcessRangeUpdate(int nodeIndex, int currentStart, int currentEnd, int queryStart,
int queryEnd, int processMode, ulong totalValue)
{
if (lazy[nodeIndex].LR != 0 || lazy[nodeIndex].LL != 0 || lazy[nodeIndex].RL != 0 || lazy[nodeIndex].RR != 0)
{
//// update tree[si] according to RR,LR,RL,LL
var updateRight = GetModuloValue(lazy[nodeIndex].RR * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LR * tree[nodeIndex].LeftStart);
var updateLeft = GetModuloValue(lazy[nodeIndex].RL * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LL * tree[nodeIndex].LeftStart);
tree[nodeIndex].LeftStart += updateLeft;
tree[nodeIndex].RightStart += updateRight;
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
// leaf node check
if (currentStart != currentEnd)
{
// Propagate Values to lazy children...
AddToLazyNode(lazy[nodeIndex * 2 + 1], lazy[nodeIndex], nodeIndex * 2 + 1);
AddToLazyNode(lazy[nodeIndex * 2 + 2], lazy[nodeIndex], nodeIndex * 2 + 2);
}
// because the update is made, set lazy values from this node to 0
lazy[nodeIndex].RR = 0;
lazy[nodeIndex].RL = 0;
lazy[nodeIndex].LL = 0;
lazy[nodeIndex].LR = 0;
}
// out of range
if (currentStart > currentEnd || currentStart > queryEnd || currentEnd < queryStart)
return;
// Current segment is fully in range
if (currentStart >= queryStart && currentEnd <= queryEnd)
{
// Update current node according to process mode
if (processMode == PROCESS_LEFT)
{
tree[nodeIndex].LeftStart += tree[nodeIndex].RightStart;
// we are only interested in the modulo value
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
}
else if (processMode == PROCESS_RIGHT)
{
tree[nodeIndex].RightStart += tree[nodeIndex].LeftStart;
// we are only interested in the modulo value
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
}
else if (processMode == PROCESS_TOTAL)
{
tree[nodeIndex].Total += totalValue;
tree[nodeIndex].LeftStart += 1;
tree[nodeIndex].RightStart += 1;
tree[nodeIndex].Total = GetModuloValue(tree[nodeIndex].Total);
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart);
}
// leaf node check
if (currentStart != currentEnd)
{
// When Update R: LR += 1 + LL; RR += RL
// When Update L: RL += 1 + RR; LL += LR;
if (processMode == PROCESS_LEFT)
{
if (tree[nodeIndex * 2 + 1].Total != 0)
{
lazy[nodeIndex * 2 + 1].RL += 1 + lazy[nodeIndex * 2 + 1].RR;
lazy[nodeIndex * 2 + 1].LL += lazy[nodeIndex * 2 + 1].LR;
lazy[nodeIndex * 2 + 1].RL = GetModuloValue(lazy[nodeIndex * 2 + 1].RL);
lazy[nodeIndex * 2 + 1].LL = GetModuloValue(lazy[nodeIndex * 2 + 1].LL);
}
if (tree[nodeIndex * 2 + 2].Total != 0)
{
lazy[nodeIndex * 2 + 2].RL += 1 + lazy[nodeIndex * 2 + 2].RR;
lazy[nodeIndex * 2 + 2].LL += lazy[nodeIndex * 2 + 2].LR;
lazy[nodeIndex * 2 + 2].RL = GetModuloValue(lazy[nodeIndex * 2 + 2].RL);
lazy[nodeIndex * 2 + 2].LL = GetModuloValue(lazy[nodeIndex * 2 + 2].LL);
}
}
else if (processMode == PROCESS_RIGHT)
{
if (tree[nodeIndex * 2 + 1].Total != 0)
{
lazy[nodeIndex * 2 + 1].LR += 1 + lazy[nodeIndex * 2 + 1].LL;
lazy[nodeIndex * 2 + 1].RR += lazy[nodeIndex * 2 + 1].RL;
lazy[nodeIndex * 2 + 1].LR = GetModuloValue(lazy[nodeIndex * 2 + 1].LR);
lazy[nodeIndex * 2 + 1].RR = GetModuloValue(lazy[nodeIndex * 2 + 1].RR);
}
if (tree[nodeIndex * 2 + 2].Total != 0)
{
lazy[nodeIndex * 2 + 2].LR += 1 + lazy[nodeIndex * 2 + 2].LL;
lazy[nodeIndex * 2 + 2].RR += lazy[nodeIndex * 2 + 2].RL;
lazy[nodeIndex * 2 + 2].LR = GetModuloValue(lazy[nodeIndex * 2 + 2].LR);
lazy[nodeIndex * 2 + 2].RR = GetModuloValue(lazy[nodeIndex * 2 + 2].RR);
}
}
}
// because the processing segment is fully in range - return
return;
}
// not completly in range (but overlaps) process children
int mid = (currentStart + currentEnd) / 2;
ProcessRangeUpdate(nodeIndex * 2 + 1, currentStart, mid, queryStart, queryEnd, processMode, totalValue);
ProcessRangeUpdate(nodeIndex * 2 + 2, mid + 1, currentEnd, queryStart, queryEnd, processMode, totalValue);
//Update this node with the results of the children
tree[nodeIndex] = MergeNodes(tree[nodeIndex * 2 + 1], tree[nodeIndex * 2 + 2]);
}
public void UpdateRange(int arrayLenth, int queryStart, int queryEnd, int processMode, ulong totalValue = 0)
{
ProcessRangeUpdate(0, 0, arrayLenth - 1, queryStart, queryEnd, processMode, totalValue);
}
// returns sum in range
private ulong ProcessSumInRange(int currentStart, int currentEnd, int queryStart, int queryEnd, int nodeIndex, int processMode)
{
if (lazy[nodeIndex].LR != 0 || lazy[nodeIndex].LL != 0 || lazy[nodeIndex].RL != 0 || lazy[nodeIndex].RR != 0)
{
//// update tree[si] according to RR,LR,RL,LL
var updateRight = GetModuloValue(lazy[nodeIndex].RR * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LR * tree[nodeIndex].LeftStart);
var updateLeft = GetModuloValue(lazy[nodeIndex].RL * tree[nodeIndex].RightStart) + GetModuloValue(lazy[nodeIndex].LL * tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart += updateRight;
tree[nodeIndex].LeftStart += updateLeft;
tree[nodeIndex].LeftStart = GetModuloValue(tree[nodeIndex].LeftStart);
tree[nodeIndex].RightStart = GetModuloValue(tree[nodeIndex].RightStart % ModuloValue);
// leaf node check
if (currentStart != currentEnd)
{
// propagate lazy data to child nodes
AddToLazyNode(lazy[nodeIndex * 2 + 1], lazy[nodeIndex], nodeIndex * 2 + 1);
AddToLazyNode(lazy[nodeIndex * 2 + 2], lazy[nodeIndex], nodeIndex * 2 + 2);
}
// lazy data is processed, set values to 0
lazy[nodeIndex].RR = 0;
lazy[nodeIndex].RL = 0;
lazy[nodeIndex].LL = 0;
lazy[nodeIndex].LR = 0;
}
// return if out of range
if (currentStart > currentEnd || currentStart > queryEnd || currentEnd < queryStart)
return 0;
// this segment completely lies in range
if (currentStart >= queryStart && currentEnd <= queryEnd)
{
if (processMode == PROCESS_LEFT)
{
return tree[nodeIndex].LeftStart;
}
else if (processMode == PROCESS_RIGHT)
{
return tree[nodeIndex].RightStart;
}
else if (processMode == PROCESS_TOTAL)
{
return tree[nodeIndex].Total;
}
}
// part of this segment overlaps with query range
int mid = (currentStart + currentEnd) / 2;
return GetModuloValue(ProcessSumInRange(currentStart, mid, queryStart, queryEnd, 2 * nodeIndex + 1, processMode) +
ProcessSumInRange(mid + 1, currentEnd, queryStart, queryEnd, 2 * nodeIndex + 2, processMode));
}
// get sum of elements in range
public ulong GetSumInRange(int arrayLength, int queryStart, int queryEnd, int processMode)
{
// Check for erroneous input values
if (queryStart < 0 || queryEnd > arrayLength - 1 || queryStart > queryEnd)
{
Console.WriteLine("GetSum - Invalid Input");
return 0;
}
return ProcessSumInRange(0, arrayLength - 1, queryStart, queryEnd, 0, processMode);
}
private void BuildSegmentTreePart(int[] arr, int currentStart, int currentEnd, int nodeIndex)
{
// return if out of range
if (currentStart > currentEnd)
return;
// only one element in current segment, create leaf node
if (currentStart == currentEnd)
{
tree[nodeIndex] = CreateNewNode(currentStart);
lazy[nodeIndex] = CreateNewLazyNode(currentStart);
return;
}
// there are more than one element
int mid = (currentStart + currentEnd) / 2;
BuildSegmentTreePart(arr, currentStart, mid, nodeIndex * 2 + 1);
BuildSegmentTreePart(arr, mid + 1, currentEnd, nodeIndex * 2 + 2);
tree[nodeIndex] = MergeNodes(tree[nodeIndex * 2 + 1], tree[nodeIndex * 2 + 2]);
lazy[nodeIndex] = CreateNewLazyNode();
}
/* Function to construct segment tree from given array.
This function allocates memory for segment tree and
calls constructSTUtil() to fill the allocated memory */
public void BuildSegmentTree(int[] arr)
{
int arrayLength = arr.Length;
int n = (int)(Math.Ceiling(Math.Log(arrayLength) / Math.Log(2)));
//Maximum size of segment tree
int maxTreeSize = 2 * (int)Math.Pow(2, n) - 1;
tree = new DPNode[maxTreeSize];
lazy = new DPLazyNode[maxTreeSize];
// Fill the allocated memory st
BuildSegmentTreePart(arr, 0, arrayLength - 1, 0);
}
private DPNode CreateNewNode(int i)
{
return new DPNode
{
Total = 0,
LeftStart = 0,
RightStart = 0,
Value = i
};
}
// The function to update a value in input array and segment tree.
// It uses updateValueUtil() to update the value in segment tree
public void ProcessArrayElement(int arrayLength, int arrayIndex)
{
// Check for erroneous input index
if (arrayIndex < 0 || arrayIndex > arrayLength - 1)
{
Console.WriteLine("Input is out of range");
return;
}
var sumLeft = arrayIndex > 0 ? GetSumInRange(arrayLength, 0, arrayIndex - 1, 0) : 0;
var sumRight = arrayIndex < arrayLength - 1 ? GetSumInRange(arrayLength, arrayIndex + 1, arrayLength - 1, 1) : 0;
var total = GetModuloValue(1 + sumLeft + sumRight);
// update current node/nest combinations
UpdateRange(arrayLength, arrayIndex, arrayIndex, 2, total);
// update nodes on the left
UpdateRange(arrayLength, 0, arrayIndex - 1, 1);
// update nodes on the right
UpdateRange(arrayLength, arrayIndex + 1, arrayLength - 1, 0);
}
private DPLazyNode CreateNewLazyNode(int i = -1)
{
return new DPLazyNode
{
LL = 0,
RR = 0,
LR = 0,
RL = 0
};
}
private DPNode MergeNodes(DPNode first, DPNode second)
{
return new DPNode
{
Total = GetModuloValue(first.Total + second.Total),
LeftStart = GetModuloValue(first.LeftStart + second.LeftStart),
RightStart = GetModuloValue(first.RightStart + second.RightStart),
Value = -1
};
}
private void AddToLazyNode(DPLazyNode child, DPLazyNode parent, int childIndex)
{
// this node is not yet processed to ignore the update data until then
if (tree[childIndex].Total == 0)
return;
var childRRUpdate = parent.RR + GetModuloValue(parent.RR * child.RR) + GetModuloValue(parent.LR * child.RL);
var childLRUpdate = parent.LR + GetModuloValue(parent.LR * child.LL) + GetModuloValue(child.LR * parent.RR);
var childLLUpdate = parent.LL + GetModuloValue(parent.LL * child.LL) + GetModuloValue(parent.RL * child.LR);
var childRLUpdate = parent.RL + GetModuloValue(parent.RL * child.RR) + GetModuloValue(child.RL * parent.LL);
child.RR += childRRUpdate;
child.LR += childLRUpdate;
child.LL += childLLUpdate;
child.RL += childRLUpdate;
child.RR = GetModuloValue(child.RR);
child.LR = GetModuloValue(child.LR);
child.LL = GetModuloValue(child.LL);
child.RL = GetModuloValue(child.RL);
}
}
public struct ValueIndex
{
public int Value { get; set; }
public int Index { get; set; }
}
public class MergeSort
{
public ValueIndex[] ValueIndex { get; set; }
private ValueIndex[] HelpValueIndex { get; set; }
private void CreateValueIndexLists(int[] array)
{
ValueIndex = new ValueIndex[array.Length];
HelpValueIndex = new ValueIndex[array.Length];
for (var i = 0; i < array.Length; i++)
{
ValueIndex[i] = new ValueIndex
{
Value = array[i],
Index = i
};
HelpValueIndex[i] = new ValueIndex
{
Value = array[i],
Index = i
};
}
}
public void Sort(int[] array, int n)
{
CreateValueIndexLists(array);
TopDownMergeSort(ValueIndex, HelpValueIndex, n);
}
public void TopDownMergeSort(ValueIndex[] sortArray, ValueIndex[] helpSortArray, int n)
{
CopyArray(sortArray, 0, n, helpSortArray);
TopDownSplitMerge(helpSortArray, 0, n, sortArray);
}
public void TopDownSplitMerge(ValueIndex[] helpSortArray, int startIndex, int endIndex, ValueIndex[] sortArray)
{
if (endIndex - startIndex < 2)
return;
var iMiddle = (endIndex + startIndex) / 2;
TopDownSplitMerge(sortArray, startIndex, iMiddle, helpSortArray);
TopDownSplitMerge(sortArray, iMiddle, endIndex, helpSortArray);
TopDownMerge(helpSortArray, startIndex, iMiddle, endIndex, sortArray);
}
public void TopDownMerge(ValueIndex[] sortArray, int startIndex, int middleIndex, int endIndex, ValueIndex[] helpSortArray)
{
int i = startIndex, j = middleIndex;
// While there are elements in the left or right runs...
for (var k = startIndex; k < endIndex; k++)
{
// If left run head exists and is <= existing right run head.
if (i < middleIndex && (j >= endIndex || sortArray[i].Value <= sortArray[j].Value))
{
helpSortArray[k] = sortArray[i];
i = i + 1;
}
else
{
helpSortArray[k] = sortArray[j];
j = j + 1;
}
}
}
public void CopyArray(ValueIndex[] A, int startIndex, int endIndex, ValueIndex[] B)
{
for (var k = startIndex; k < endIndex; k++)
B[k] = A[k];
}
}
class Solution {
public int solution(int[] H) {
// write your code in C# 6.0 with .NET 4.5 (Mono)
var N = H.Length;
// the array is empty there are no combinations
if (N == 0)
return 0;
if (N == 1)
return 1;
// for this solution I'm using Lazy Propagation Segment tree
// Complexity to build is O(n)
// Complexity of one query update is O(logn) so in total for our calculations we'll have complexity of O(nlogn)
// Space needed for building Segment Tree is O(k*N) where k in interval (2-3)
LazySegmentTree tree = new LazySegmentTree();
// Build segment tree from given array
tree.BuildSegmentTree(H);
// I use Merge Sort because it has time complexity of O(nlogn) and space complexity of O(n)
var mergeSort = new MergeSort();
mergeSort.Sort(H, H.Length);
// process the elements in ascending oreder
for (int i = 0; i < H.Length; i++)
{
tree.ProcessArrayElement(H.Length, mergeSort.ValueIndex[i].Index);
}
var ret = tree.GetSumInRange(N, 0, N - 1, 2);
return Convert.ToInt32(ret % 1000000007);
}
}
The solution obtained perfect score.
Small random tests, failing exponential-time solutions and depending on the size of values. Requires int64
Small random tests, failing exponential-time solutions and depending on the size of values.