Challenge: To Heap or Not to Heap
Subdomeniu: Combinatorics (combinatorics)
Scor cont: 100.0 / 100
Submission status: Accepted
Submission score: 1.0
Submission ID: 464819261
Limbaj: cpp14
Link challenge: https://www.hackerrank.com/challenges/to-heap-or-not-to-heap/problem
Cerinta
Consider a rooted binary tree with $n$ vertices containing numbers. Each vertex of the tree either has two sons (left son and right son), or no sons. We will call such a tree _heap_, if and only if for all vertices (except the root), the number assigned the vertex is smaller or equal to the parent's number.
Consider a heap and the following function:
dfs(vertex){
print number in the vertex
if (vertex is not a leaf) {
dfs(left son of the vertex)
dfs(right son of the vertex)
}
}
You are given a sequence $a[1..n]$ of $n$ numbers. Your task is to calculate how many heaps will produce this sequence after calling `dfs(root)`. It is guaranteed that the sequence is generated by `generate()` function listed in the input format section below. Since the number of heaps can be very large, output its value modulo $1000000007~(10^9 + 7)$.
**Constraints**
$1 \le n < 2 \times 10^5$
$1 \le a_i \le n$
Input Format
The first line contains a single odd integer $n$. The second line contains $n$ space-separated integers $a_1, a_2, \dots, a_n$ $-$ the result of `dfs(root)` call.
The sequence is generated by this algorithm:
int n, k, ptr
array of integers a[1 .. n]
generate(){
read odd n
create array val[1 .. n]
for each i from 1 to n
val[i] = random(1, n) //random(l, r) returns uniform integer from [l, r]
ptr = 1
sort array val by non-increasing
gen_heap(val)
}
gen_heap(array values){
k = size of values
a[ptr] = values[1]
ptr = ptr + 1
if(k == 1)
return
create two empty arrays left, right
for each i from 2 to k - 1
if(random(1, 2) == 1){
add values[i] to the end of left
}else{
add values[i] to the end of right
}
if(left has even size)
add values[k] to the end of left
else
add values[k] to the end of right
gen_heap(left);
gen_heap(right);
}
Output Format
Output the number of heaps that will produce the given sequence modulo $1000000007~(10^9 + 7)$.
Cod sursa
#include <bits/stdc++.h>
using namespace std;
static const int MOD = 1000000007;
struct Frame {
int l, r;
int phase; // 0=init, 1=iterate splits
int curL;
int endL;
int maxL;
long long ans;
};
static inline long long keyOf(int l, int r) {
return ( (long long)l << 21 ) ^ (long long)r; // n < 2e5, 21 bits is safe
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<int> a(n);
for (int i = 0; i < n; ++i) cin >> a[i];
if (n == 1) {
cout << 1 << '\n';
return 0;
}
int maxK = (n - 1) / 2;
vector<int> inv(maxK + 2, 0), catalan(maxK + 1, 0);
inv[1] = 1;
for (int i = 2; i <= maxK + 1; ++i) {
inv[i] = MOD - (long long)(MOD / i) * inv[MOD % i] % MOD;
}
catalan[0] = 1;
for (int k = 1; k <= maxK; ++k) {
catalan[k] = (long long)catalan[k - 1] * (4LL * k - 2) % MOD * inv[k + 1] % MOD;
}
// next strictly greater element index
vector<int> bigger(n, n);
vector<int> st;
st.reserve(n);
for (int i = 0; i < n; ++i) {
while (!st.empty() && a[st.back()] < a[i]) {
bigger[st.back()] = i;
st.pop_back();
}
st.push_back(i);
}
// prefix count of strict increases
vector<int> inc(n, 0);
for (int i = 1; i < n; ++i) {
inc[i] = inc[i - 1] + (a[i - 1] < a[i] ? 1 : 0);
}
unordered_map<long long, int> memo;
memo.reserve((size_t)n * 8);
memo.max_load_factor(0.7f);
vector<Frame> stackFrames;
stackFrames.reserve((size_t)n * 4);
stackFrames.push_back({0, n - 1, 0, 0, 0, 0, 0});
while (!stackFrames.empty()) {
Frame &f = stackFrames.back();
long long key = keyOf(f.l, f.r);
auto itMemo = memo.find(key);
if (itMemo != memo.end()) {
stackFrames.pop_back();
continue;
}
if (f.phase == 0) {
int len = f.r - f.l + 1;
if (f.l >= f.r) {
memo[key] = 1;
stackFrames.pop_back();
continue;
}
if ((len & 1) == 0) {
memo[key] = 0;
stackFrames.pop_back();
continue;
}
// root must be maximum of interval (no strictly greater inside)
if (bigger[f.l] <= f.r) {
memo[key] = 0;
stackFrames.pop_back();
continue;
}
// fully non-increasing segment -> Catalan shortcut
if (inc[f.r] - inc[f.l] == 0) {
memo[key] = catalan[(len - 1) / 2];
stackFrames.pop_back();
continue;
}
f.phase = 1;
f.curL = 1;
f.maxL = bigger[f.l + 1] - (f.l + 1);
f.endL = min(len - 1, f.maxL + 1); // iterate L in [1, endL) by +2
f.ans = 0;
}
bool pushed = false;
while (f.curL < f.endL) {
int L = f.curL;
int l1 = f.l + 1;
int r1 = f.l + L;
int l2 = f.l + L + 1;
int r2 = f.r;
// right subtree root must be maximum on [l2..r2]
if (bigger[l2] <= r2) {
f.curL += 2;
continue;
}
long long k1 = keyOf(l1, r1);
auto it1 = memo.find(k1);
if (it1 == memo.end()) {
stackFrames.push_back({l1, r1, 0, 0, 0, 0, 0});
pushed = true;
break;
}
long long k2 = keyOf(l2, r2);
auto it2 = memo.find(k2);
if (it2 == memo.end()) {
stackFrames.push_back({l2, r2, 0, 0, 0, 0, 0});
pushed = true;
break;
}
f.ans = (f.ans + 1LL * it1->second * it2->second) % MOD;
f.curL += 2;
}
if (pushed) continue;
memo[key] = (int)f.ans;
stackFrames.pop_back();
}
cout << memo[keyOf(0, n - 1)] << '\n';
return 0;
}
HackerRank Combinatorics – To Heap or Not to Heap
