This is the question link - QSET - Codechef
This is the editorial link - QSET - Editorial
Basically the question queries for the number of substring in some range [L, R]. I have implemented a segment tree to solve this question. I have closely followed the editorial.
I have created a struct
to represent a node of the segment tree.
Can someone explain to me how to make this program faster? I'm guessing faster I/O is the key here. Is that so?
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#define ll long long
using namespace std;
struct stnode
{
ll ans; // the answer for this interval
ll pre[3]; // pre[i] denotes number of prefixes of interval which modulo 3 give i
ll suf[3]; // suf[i] denotes number of suffixes of interval which modulo 3 give i
ll total; // sum of interval modulo 3
void setLeaf(int value)
{
if (value % 3 == 0) ans = 1;
else ans = 0;
pre[0] = pre[1] = pre[2] = 0;
suf[0] = suf[1] = suf[2] = 0;
pre[value % 3] = 1;
suf[value % 3] = 1;
total = value % 3;
}
void merge(stnode leftChild, stnode rightChild)
{
ans = leftChild.ans + rightChild.ans;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if ((i + j) % 3 == 0) ans += leftChild.suf[i] * rightChild.pre[j];
pre[0] = pre[1] = pre[2] = 0;
suf[0] = suf[1] = suf[2] = 0;
for (int i = 0; i < 3; i++)
{
pre[i] += leftChild.pre[i] + rightChild.pre[(3 - leftChild.total + i) % 3];
suf[i] += rightChild.suf[i] + leftChild.suf[(3 - rightChild.total + i) % 3];
}
total = (leftChild.total + rightChild.total) % 3;
}
} segtree[400005];
void buildST(string digits, int si, int ss, int se)
{
if (ss == se)
{
segtree[si].setLeaf(digits[ss] - '0');
return;
}
long left = 2 * si + 1, right = 2 * si + 2, mid = (ss + se) / 2;
buildST(digits, left, ss, mid);
buildST(digits, right, mid + 1, se);
segtree[si].merge(segtree[left], segtree[right]);
}
stnode getValue(int qs, int qe, int si, int ss, int se)
{
if (qs == ss && se == qe)
return segtree[si];
stnode temp;
int mid = (ss + se) / 2;
if (qs > mid)
temp = getValue(qs, qe, 2 * si + 2, mid + 1, se);
else if (qe <= mid)
temp = getValue(qs, qe, 2 * si + 1, ss, mid);
else
{
stnode temp1, temp2;
temp1 = getValue(qs, mid, 2 * si + 1, ss, mid);
temp2 = getValue(mid + 1, qe, 2 * si + 2, mid + 1, se);
temp.merge(temp1, temp2);
}
return temp;
}
void updateTree(int si, int ss, int se, int index, int new_value)
{
if (ss == se)
{
segtree[si].setLeaf(new_value);
return;
}
int mid = (ss + se) / 2;
if (index <= mid)
updateTree(2 * si + 1, ss, mid, index, new_value);
else
updateTree(2 * si + 2, mid + 1, se, index, new_value);
segtree[si].merge(segtree[2 * si + 1], segtree[2 * si + 2]);
}
int main()
{
ios_base::sync_with_stdio(false);
int n, m; cin >> n >> m;
string digits; cin >> digits;
buildST(digits, 0, 0, n - 1);
while (m--)
{
int q; cin >> q;
if (q == 1)
{
int x; int y; cin >> x >> y;
updateTree(0, 0, n - 1, x - 1, y);
}
else
{
int c, d; cin >> c >> d;
cout << getValue(c-1, d-1, 0, 0, n - 1).ans << '\n';
}
}
}
I am getting TLE for larger test cases, ie subtasks 3 and 4 (check the problem page). For subtasks 1 and 2, it gets accepted.
[www.codechef.com/viewsolution/5909107] is an accepted solution. It has pretty much the same code structure except that scanf
is used instead of cin
. But, I turned off the sync_with_stdio
so that shouldn't be a differentiator, right?
I found out what was making this program slow. In the buildST
function, I pass the string
digits
. Since the function is recursive, and the input is fairly large, this creates many copies of the string digits
thus incurring large overhead.
I declared a char digits[]
at the start of the program and modified the method buildST
as follows (basically same but without string digits
as a parameter:
void buildST(int si, int ss, int se)
{
if (ss == se)
{
segtree[si].setLeaf(digits[ss] - '0');
return;
}
long left = 2 * si + 1, right = 2 * si + 2, mid = (ss + se) / 2;
buildST(left, ss, mid);
buildST(right, mid + 1, se);
segtree[si].merge(segtree[left], segtree[right]);
}
This solution got accepted.