So, after researching and talking to some people, I came up with an answer that runs fast enough to solve the problem in the given time of the puzzle.
The readability still poor on the main, but this code is for coding marathons, readability is not something you should really care when writing code for something like this. But the functions that I got to solve the problem are well written, so I think it will be clear.
The code:
#include <cstdio>
int A[1010101];
int tree[1010101];
int lazy[1010101];
void build(int node, int start, int end)
{
if(start == end)
{
// Leaf node will have a single element
tree[node] = A[start];
}
else
{
int mid = (start + end) / 2;
// Recurse on the left child
build(2*node, start, mid);
// Recurse on the right child
build(2*node+1, mid+1, end);
// Internal node will have the sum of both of its children
tree[node] = tree[2*node] + tree[2*node+1];
}
}
void updateRange(int node, int start, int end, int l, int r, int val)
{
if(lazy[node] != 0)
{
// This node needs to be updated
tree[node] += (end - start + 1) * lazy[node]; // Update it
if(start != end)
{
lazy[node*2] += lazy[node]; // Mark child as lazy
lazy[node*2+1] += lazy[node]; // Mark child as lazy
}
lazy[node] = 0; // Reset it
}
if(start > end or start > r or end < l) // Current segment is not within range [l, r]
return;
if(start >= l and end <= r)
{
// Segment is fully within range
tree[node] += (end - start + 1) * val;
if(start != end)
{
// Not leaf node;
lazy[node*2] +=val;
lazy[node*2+1] += val;
}
return;
}
int mid = (start + end) / 2;
updateRange(node*2, start, mid, l, r, val); // Updating left child
updateRange(node*2 + 1, mid + 1, end, l, r, val); // Updating right child
tree[node] = tree[node*2] + tree[node*2+1]; // Updating root with max value
}
int queryRange(int node, int start, int end, int l, int r)
{
if(start > end or start > r or end < l)
return 0; // Out of range
if(lazy[node] != 0)
{
// This node needs to be updated
tree[node] += (end - start + 1) * lazy[node]; // Update it
if(start != end)
{
lazy[node*2] += lazy[node]; // Mark child as lazy
lazy[node*2+1] += lazy[node]; // Mark child as lazy
}
lazy[node] = 0; // Reset it
}
if(start >= l and end <= r) // Current segment is totally within range [l, r]
return tree[node];
int mid = (start + end) / 2;
int p1 = queryRange(node*2, start, mid, l, r); // Query left child
int p2 = queryRange(node*2 + 1, mid + 1, end, l, r); // Query right child
return (p1 + p2);
}
int main(){
int n,m;
scanf("%d %d", &n, &m);
for (int i = 0; i < n; ++i)
scanf("%d", &A[i]);
build(1,0,n-1);
for (int i = 0; i < m; ++i)
{
int idx;
scanf("%d",&idx);
int count = queryRange(1,1,n,idx,idx);
updateRange(1,0,n-1,
(idx -1 -count >=0 ? idx -1 -count : 0),
(idx + count <=n ? idx + count : n)-1,-1);
}
long long sum =0;
for (int i = 1; i <= n; i++) {
int leafval=queryRange(1,1,n,i,i);
sum += leafval > 0 ? leafval : 0;
}
printf("%d\n", sum);
}
The solution is based on a segment tree, more specifically the lazy segment tree implementation.
When I need to update a range, I just update the tree branches that correspond to the range, and mark the children as "dirty".
Every time I need to access a node, I have to check if the node is dirty, and update it when needed.
This way, the update runs at O(log N)
, so, in the worst case, it will be O(M * log N)
for the updates.
After that I need to force update of every leaf of the tree, because I have to check if any leaf got a negative value, and discart them. This costs O(N * log N)
.
On the end, the code is O((M * log N) + (N * log n))
.