0

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?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Saket Mehta
  • 2,438
  • 2
  • 23
  • 28
  • I think this may be off-topic because questions about improving working code belong to http://codereview.stackexchange.com/ – Baum mit Augen Jan 17 '15 at 13:58
  • Please thouroughly check the [info](http://stackoverflow.com/tags/codechef/info) in the [tag:codechef] tag wiki. – πάντα ῥεῖ Jan 17 '15 at 13:59
  • @BaummitAugen How do I move my question to codereview.stackexchange.com without it being considered cross-posting. – Saket Mehta Jan 17 '15 at 14:05
  • 1
    CodeChef has [their own SO](http://discuss.codechef.com) "Discussing problems or any aspect of problem, on any other platform on web, on identification, could lead to disabling of respective account and banning from the community." – Raymond Chen Jan 17 '15 at 14:08
  • @Saket Well since it is not closed (yet), you can consider your question on-topic for now I would say. (It needs 5 non-mods to close a question, I only have one vote others might or might not agree with.) If your question should be closed and not moved by a mod, I think you could simply repost it on codereview. (No guarantee on this though, I don't know all moderation rules of SO.) – Baum mit Augen Jan 17 '15 at 14:13
  • @RaymondChen That is when the contest is on-going. – Saket Mehta Jan 17 '15 at 14:16
  • But still, they have their own SO. Would fit better there. – Raymond Chen Jan 17 '15 at 14:17
  • @RaymondChen Actually I did ask this question over there. Not exactly a constructive response. [link] (http://discuss.codechef.com/questions/61934/in-the-problem-qset-in-jan15-long-i-used-segment-trees-and-still-tle-please-explain) – Saket Mehta Jan 17 '15 at 14:22

1 Answers1

0

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.

Saket Mehta
  • 2,438
  • 2
  • 23
  • 28