AtCoder Regular Contest 063 review

Link

C – 一次元リバーシ / 1D Reversi

/*
 * AC	6 ms	512 KB
 */
#include <cstdio>
#include <iostream>

using namespace std;

int main(){
    //freopen("c.in", "r", stdin);
    string s;
    cin >> s;
    int ans = 0;
    for (int i = 0; i < s.size() - 1; ++i) {
        if (s[i] != s[i+1]) ans++;
    }
    printf("%d\n", ans);
    return 0;
}

D – 高橋君と見えざる手 / An Invisible Hand

I didn’t notice that there is a restriction that each \(A_i\) is different. So it took time for implementation.

/*
 * AC	15 ms	640 KB
 */
#include <cstdio>
#include <iostream>

using namespace std;

#define INF 1e9;

int a[100005];
int N, T;
int maxDiff = 0;

int main(){
    //freopen("d3.in", "r", stdin);

    scanf("%d %d", &N, &T);
    int tmpmin = INF;
    for (int i = 0; i < N; ++i) {
        scanf("%d", &a[i]);
        if (tmpmin > a[i]) tmpmin = a[i];
        int diff = a[i] - tmpmin;
        if (diff > maxDiff) maxDiff = diff;
    }

    int minCount = 0, maxCount = 0;
    tmpmin = INF;
    int ans = 0;
    for (int i = 0; i < N; ++i) {
        if (tmpmin + maxDiff == a[i]) maxCount++;
        if (tmpmin == a[i]) {
            minCount++;
        } else if (tmpmin > a[i]) {
            // update
            tmpmin = a[i];
            ans += min(minCount, maxCount);
            // re-init
            minCount = 1;
            maxCount = 0;
        }
    }
    ans += min(minCount, maxCount);
    printf("%d\n", ans);
    return 0;
}

E – 木と整数 / Integers on a Tree

Follow editorial.

/*
 * AC	78 ms	14208 KB
 */
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

#define INF 1e9

int N, K;
vector<int> edge[100005]; // edge[i] connect from i to edge[i][j].
int v[100005];   // v[i] is written in vertex i
int par[100005]; // vertex i's parent is par[i]
int lb[100005], ub[100005];  // Range [lb[i], ub[i]] is allowed at vertex i, v[i] must be in [lb[i], ub[i]]

int root;

void dfs(int vertex) {
    for (int i = 0; i < edge[vertex].size(); ++i) {
        int nextVertex = edge[vertex][i];
        if (nextVertex != par[vertex]) {
            par[nextVertex] = vertex;
            dfs(nextVertex);
        } else {
            // skip this
        }
    }

    for (int i = 0; i < edge[vertex].size(); ++i) {
        int nextVertex = edge[vertex][i];
        lb[vertex] = max(lb[vertex], lb[nextVertex] - 1);
        ub[vertex] = min(ub[vertex], ub[nextVertex] + 1);
    }
}

// Decide the example of v[i]
void dfs2(int vertex) {
    for (int i = 0; i < edge[vertex].size(); ++i) {
        int nextVertex = edge[vertex][i];
        if (nextVertex != par[vertex]) {
            par[nextVertex] = vertex;
            int tmp;
            tmp = v[vertex] + 1;
            if (lb[nextVertex] <= tmp && tmp <= ub[nextVertex]) {
                v[nextVertex] = tmp;
            } else {
                tmp = v[vertex] - 1;
                if (lb[nextVertex] <= tmp && tmp <= ub[nextVertex]) {
                    v[nextVertex] = tmp;
                } else {
                    // Error, impossible
                    lb[root] = INF;
                    ub[root] = -INF;
                    return;
                }
            }
            dfs2(nextVertex);
        } else {
            // skip this
        }
    }
}

int main(){
    //freopen("e3.in", "r", stdin);
    scanf("%d", &N);
    int a, b;
    for (int i = 0; i < N-1; ++i) {
        scanf("%d %d", &a, &b);
        edge[a].push_back(b);
        edge[b].push_back(a);

    }
    // init
    for (int i = 1; i <= N; ++i) {
        v[i] = INF;
        lb[i] = -INF;
        ub[i] = INF;
    }

    scanf("%d", &K);
    for (int i = 0; i < K; ++i) {
        int vertex, p;
        scanf("%d %d", &vertex, &p);
        v[vertex] = p;
        lb[vertex] = ub[vertex] = p;
        root = vertex;
    }
    par[root] = root;
    dfs(root);
    dfs2(root);

    if (lb[root] == v[root] && ub[root] == v[root]) {
        // possible
        printf("Yes\n");
        for (int i = 1; i <=N; ++i) {
            printf("%d\n", v[i]);
        }
    } else {
        printf("No\n");
    }



    return 0;
}

Codeforces Round #378 Div. 2 review

Link

A. Grasshopper And the String

  • Link: Problem
  • Category: Implementation

Even this is a first problem, I get wrong answer for first time submission. It is necessary to consider a corner case at the “end of the text”.

Some other solution is inserting extra ‘A’ at the end of text so that you can handle it in the same way. (ex. latte0119’s solution)

/*
 * Accepted	15 ms	0 KB
 *
 * Need to take care the corner case at the end - '\0' works similarly with the vowels.
 */
#include <cstdio>
#include <iostream>

using namespace std;

char s[105];

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in", "r", stdin);
#endif
    scanf("%s", s);
    int tmp = 0;
    int ability = 0;
    for (int i = 0; i < 105; ++i) {
        tmp++;
        if (s[i] == 'A' || s[i] == 'I' || s[i] == 'U' || s[i] == 'E' || s[i] == 'O' || s[i] == 'Y'
                || s[i] == '\0') {
            ability  = max(ability, tmp);
            tmp = 0;
            if (s[i] == '\0') break;
        }
    }

    printf("%d\n", ability);
    return 0;
}

B. Parade

Below implementation is a little bit optimized solution for not to use memory of \(O(N)\), but use \(O(1)\). But in the contest, it is better to consider solve it as fast as possible rather than optimize too much.
/*
 * Accepted	31 ms	0 KB
 */
#include <cstdio>
#include <iostream>

using namespace std;

int main(){
#ifndef ONLINE_JUDGE
    freopen("b3.in", "r", stdin);
#endif
    int n;
    scanf("%d", &n);

    int maxb = 0, minb = 0;
    int maxIndex = -1, minIndex = -1;
    int B = 0; // total

    for (int i = 0; i < n; ++i) {
        int l, r;
        scanf("%d %d", &l, &r);
        int tmpb = l - r;
        if (tmpb > maxb) { maxb = tmpb; maxIndex = i; }
        if (tmpb < minb) { minb = tmpb; minIndex = i;}
        B += tmpb;
    }
    int ans = 0;
    int maxB = abs(B);
    if (maxB < abs(B-2*maxb)) { maxB = abs(B-2*maxb); ans = maxIndex+1; }
    if (maxB < abs(B-2*minb)) { maxB = abs(B-2*minb); ans = minIndex+1; }

    printf("%d\n", ans);
    return 0;
}

C. Epidemic in Monstropolis

When we compare the sequence of \(a\) and \(b\), we notice that \(b_1\) is made of the group from \(a_1\) to \(a_{k_1}\), \(b_2\) is made of the group from \(a_{k_1+1}\) to \(a_{k_2}\), and so on. So we can just check it can be done or not with greedy algorithm.

The implementation amount is big, so it is difficult to solve it in time for me. 

/*
 * Accepted	15 ms	0 KB
 */
#include <cstdio>
#include <iostream>
#include <list>

using namespace std;

int a[505];
int b[505];

// for answer
int x[505]; // index #of the monster eat
char direction[505]; // eat direction

int main(){
#ifndef ONLINE_JUDGE
    freopen("c2.in", "r", stdin);
#endif
    int n, k;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
    scanf("%d", &k);
    for (int i = 0; i < k; ++i) scanf("%d", &b[i]);

    int scanAL = 0, scanA = 0, scanB = 0;
    int action = 0;

    int A = 0;
    bool possible = true;

    while (scanA != n && scanB != k) {
        A += a[scanA];
        if (A == b[scanB]) {
            int maxA = 0;
            int winner = -1;
            bool left = false; // start to eat from left.

            for (int i = scanAL; i <= scanA; ++i) {
                maxA = max(maxA, a[i]);
            }

            if (scanAL == scanA) {
                // corner case: if already only 1 winner exists in this group
                // do nothing
            } else {
                for (int i = scanAL; i <= scanA; ++i) {
                    if (a[i] == maxA) {
                        //cout << "maxA " << maxA << ", a[i] " << a[i] << endl;
                        if (i != scanAL && a[i-1] < a[i]) {
                            winner = i - scanAL;
                            left = true;
                            break;
                        } else if (i != scanA && a[i+1] < a[i]) {
                            winner = i - scanAL;
                            left = false;
                            break;
                        }
                    }
                }

                if (winner == -1) {
                    possible = false;
                    break;
                } else {
                    for (int i = 0; i < scanA - scanAL; ++i) {
                        // winner eat all the other monsters.
                        if (winner > 0 && left) {
                            // eat L
                            x[action] = scanB + winner + 1;
                            direction[action] = 'L';
                            action++;

                            winner--;
                        } else {
                            // eat R
                            x[action] = scanB + winner + 1;
                            direction[action] = 'R';
                            action++;
                            left = true;// now possible to eat left monster
                        }
                        //cout << "scanB: " << scanB << " " << "winner: " << winner << endl;
                    }
                }
            }

            scanAL = scanA+1;
            scanB++;
            A = 0;
        } else if (A > b[scanB]) {
            // impossible
            possible = false;
            break;
        }
        scanA++;
    }
    if (scanA != n || scanB != k) possible = false;

    if (possible) {
        printf("YES\n");
        for (int i = 0; i < n - k; ++i) {
            printf("%d %c\n", x[i], direction[i]);
        }
    } else {
        printf("NO\n");
    }
    return 0;
}

D. Kostya the Sculptor

  • Link: Problem
  • Category: data structures – use map, normalized representaion.

Since this problem restricts to consider only 1 or 2 parallelepipe combination, we can check all the posibility. To check 2 parallelepipe combination, it takes \(O(n^2)\) with straight-forward implementation, but by using map we can reduce the search time and it can be done in \(O(nlog(n))\) time.

map‘s key must be comparable, so I used pair<int, int>.

/*
 * Accepted	264 ms	11800 KB
 *
 * O(n log(n))
 */
#include <cstdio>
#include <iostream>
#include <map>

using namespace std;

typedef pair<int, int> P;
map<P, P> stoneMap; // key (1, 2): edge 1 and edge 2. value (first, second): 1st = edge 3, 2nd = index.

int ansNum = 0;
int ans1 = 0, ans2 = 0;
int maxR = 0;

P make_normalized_pair(int a, int b) {
    if (a <= b) return make_pair(a, b);
    else return make_pair(b, a);
}

void check_combination(int a, int b, int c, int i) {
    auto it = stoneMap.find(make_normalized_pair(a,b));
    if (it != stoneMap.end()) {
        P key = it->first;
        P value = it->second;

        // key.first <= key.second always, no need to check key.second
        int r = min(key.first, value.first + c);
        if (maxR < r) {
            maxR = r;
            ansNum = 2;
            ans1 = value.second;
            ans2 = i;
        }
        if (value.first < c) stoneMap[make_normalized_pair(a,b)] = make_pair(c, i);  // update this map
    } else {// not found
        stoneMap[make_normalized_pair(a,b)] = make_pair(c, i);
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("d2.in", "r", stdin);
#endif
    int n;
    scanf("%d", &n);
    int a, b, c;

    for (int i = 1; i <= n; ++i) {
        scanf("%d %d %d", &a, &b, &c);
        int r = min(min(a, b), c);

        // check this parallelepiped
        if (maxR < r) {
            maxR = r;
            ansNum = 1;
            ans1 = i;
        }
        // check 2 combination
        check_combination(a, b, c, i);
        check_combination(b, c, a, i);
        check_combination(c, a, b, i);
    }

    printf("%d\n", ansNum);
    if (ansNum == 1) {
        printf("%d\n", ans1);
    } else {
        printf("%d %d\n", ans1, ans2);
    }

    return 0;
}

Codeforces Round #377 Div. 2 review

Link

A. Buy a Shovel

It can be paid without change if the change is 0 or \(r\).

#include <cstdio>
#include <iostream>

using namespace std;

int main(){
#ifndef ONLINE_JUDGE
    freopen("a3.in", "r", stdin);
#endif
    int k, r;
    scanf("%d %d", &k, &r);

    int i;
    for (i = 1; i < 10; ++i) {
        int change = k * i % 10;
        if (change == 0 || change == r) break;
    }
    printf("%d", i);
    return 0;
}

B. Cormen — The Best Friend Of a Man

At day \(i\), dog need to walk \(k−a[i−1]\) times.
/*
 *
 */
#include <cstdio>
#include <iostream>

using namespace std;

int a[505];

int main(){
#ifndef ONLINE_JUDGE
    freopen("b3.in", "r", stdin);
#endif
    int n, k;
    scanf("%d %d", &n, &k);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &a[i]);
    }

    int inc = 0;
    for (int i = 1; i < n; ++i) {
        if (a[i-1] + a[i] >= k) continue;
        inc += k - a[i-1] - a[i];
        a[i] = k - a[i-1];
    }
    printf("%d\n", inc);
    for (int i = 0; i < n; ++i) {
        printf("%d%c", a[i], i==n-1 ? '\n' : ' ');
    }

    return 0;
}

C. Sanatorium

Basic idea to the solution is not difficult, just check all the popssibility of what time start eating (enter room) and what time end eating (exit room). But the corner case handling makes this problem difficult and I cannot answer correctly in one time.

Rather than below, more elegant/ short code solution can be found at pekempey’s solution.

#include <cstdio>
#include <iostream>

using namespace std;
typedef long long ll;

int main(){
#ifndef ONLINE_JUDGE
    freopen("c4.in", "r", stdin);
#endif
    ll b[3];
    cin >> b[0] >> b[1] >> b[2];

    ll minimum = 3000000000000000000LL;
    for (int i = 0; i < 3; ++i) {// 0: 0,0,0. 1: 0,0,1, 2: 0,1,1
        for (int k = 0; k < i; ++k) { b[2-k]--; }
        for (int j = 0; j < 3; ++j) {// 0: 0,0,0. 1: 1,0,0, 2: 1,1,0
            for (int k = 0; k < j; ++k) { b[k]--; }
            ll missed = 0;
            ll middleDay = max(max(b[0], b[1]), b[2]);
            if (middleDay >= 0) {
                missed += middleDay*3 - (b[0]+b[1]+b[2]);
                minimum = min(minimum, missed);
            }
            for (int k = 0; k < j; ++k) { b[k]++; }
        }
        for (int k = 0; k < i; ++k) { b[2-k]++; }
    }
    if (b[0] == 0 && b[1] == 1 && b[2] == 0) minimum = 0;
    cout << minimum;
    return 0;
}

D. Exams

Use binary search. Fix the duration x days, and we can check if it is possible to pass all the tests in x days or not. 

In each check it costs \(O(N)\), so in total it takes \(O(NlogN)\) to find the solution.
/*
 * Binary search
 * O(NlogN)
 *
 * Check if all tests can be passed by n days or not.
 */
#include <cstdio>
#include <iostream>

using namespace std;

int n, m;
int d[100005];
int a[100005];
bool used[100005];

//Check if all tests can be passed by x days or not.
bool C(int x) {
    used[0] = true;
    for (int i = 1; i <= m; ++i) {
        used[i] = false; // init
    }
    int passed = 0;  // #of passed subject
    int day = 0;     // total necessary preparation days
    for (int i = x - 1; i >= 0; --i) {
        if (!used[d[i]]) { // take exam of d[i]
            passed++;
            day += a[d[i]];
            used[d[i]] = true;
        } else {  // study subject of used[d[i]] == true.
            if(day > 0) day--;
        }
    }
    if (passed == m && day == 0) return true;
    else return false;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("d3.in", "r", stdin);
#endif
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &d[i]);
    }
    for (int i = 1; i <= m; ++i) {
        scanf("%d", &a[i]);
    }
    int lb=0, ub=n;  // can do between (lb, ub]
    while (ub - lb > 1) {
        int middle = (lb+ub)/2;
        if (C(middle)) ub = middle;
        else lb = middle;
        //cout << lb << " " << ub << " " << middle << endl;
    }
    if (ub == n && !C(n)) ub = -1;
    printf("%d\n", ub);

    return 0;
}

AtCoder Regular Contest 062 review

Link

C – AtCoDeerくんと選挙速報 / AtCoDeer and Election Report

/*
 *
 */
#include <cstdio>
#include <iostream>

using namespace std;

typedef long long ll;

ll t[1005];
ll a[1005];

int main(){
    //freopen("c3.in", "r", stdin);
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%lli %lli", &t[i], &a[i]);
    }
    ll currentT = 1, currentA = 1;
    for (int i = 0; i < n; ++i) {
        ll multiT = (currentT - 1LL) / t[i] + 1LL;
        ll multiA = (currentA - 1LL) / a[i] + 1LL;
        ll multi = max(multiT, multiA);
        currentT = t[i] * multi;
        currentA = a[i] * multi;
        //printf("%d %lli %lli %lli %lli\n", i, multiT, multiA, currentT, currentA);
    }
    printf("%lli\n", currentT + currentA);
    return 0;
}

D – AtCoDeerくんと変なじゃんけん / AtCoDeer and Rock-Paper

/*
 *
 */
#include <cstdio>
#include <iostream>

using namespace std;

int main(){
    freopen("d2.in", "r", stdin);
    string s;
    cin >> s;

    int totalWin = 0;
    for (int i = 0; i < s.size(); ++i) {
        if (i%2 == 0) {
            if (s[i] == 'g') {

            } else {
                totalWin--;
            }
        } else {
            if (s[i] == 'g') {
                totalWin++;
            }
        }
    }

    printf("%d\n", totalWin);
    return 0;
}

E – AtCoDeerくんと立方体づくり / Building Cubes with AtCoDeer

Implementation based on Editorial.

/*
 * Ref: editorial - http://arc062.contest.atcoder.jp/data/arc/062/editorial.pdf
 */
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>

#define MAX_N 405
#define MAX_C 1000

using namespace std;
typedef long long ll;

int c[MAX_N][4];

ll calc(ll c0, ll c1, ll c2, ll c3) {
    return c0 * MAX_C * MAX_C * MAX_C + c1  * MAX_C * MAX_C + c2 * MAX_C + c3;
}

ll normalizeC(int* start) {
    ll minimum = calc(start[0], start[1], start[2], start[3]);
    for (int i = 0; i < 4; ++i) {
        ll next = calc(start[(0+i)%4], start[(1+i)%4], start[(2+i)%4], start[(3+i)%4]);
        if (minimum > next) {
            minimum = next;
        }
    }
    return minimum;
}

ll normalizeC(int a, int b, int c, int d) {
    int s[4] = {a, b, c, d};
    return normalizeC(s);
}

ll multi(int a, int b, int c, int d) {
    if (a==b && b==c && c==d) return 4;
    if (a==c && b == d) return 2;
    else return 1;
}

int main(){
    //freopen("e3.in", "r", stdin);
    int n;
    scanf("%d", &n);

    map<ll, int> tileMap;
    for (int i = 0; i < n; ++i) {
        scanf("%d %d %d %d", &c[i][0], &c[i][1], &c[i][2], &c[i][3]);
        ll key = normalizeC(c[i]);
        tileMap[key]++;
        //cout << key << ": " <<tileMap[key] << endl;
    }

    ll total = 0;
    for (int i = 0; i < n; ++i) {        // Determine side '1'
        for (int j = i+1; j < n; ++j) {  // Determine side '5'
            ll key1 = normalizeC(c[i]);
            ll key5 = normalizeC(c[j]);
            tileMap[key1]--;
            tileMap[key5]--;
            for (int k = 0; k < 4; ++k) {// Determine rotation of '5'
                ll key2 = normalizeC(c[i][0], c[j][(1+k)%4], c[j][(0+k)%4], c[i][1]);
                ll multiple2 = multi(c[i][0], c[j][(1+k)%4], c[j][(0+k)%4], c[i][1]);
                ll key3 = normalizeC(c[i][1], c[j][(0+k)%4], c[j][(3+k)%4], c[i][2]);
                ll multiple3 = multi(c[i][1], c[j][(0+k)%4], c[j][(3+k)%4], c[i][2]);
                ll key4 = normalizeC(c[i][2], c[j][(3+k)%4], c[j][(2+k)%4], c[i][3]);
                ll multiple4 = multi(c[i][2], c[j][(3+k)%4], c[j][(2+k)%4], c[i][3]);
                ll key6 = normalizeC(c[j][(1+k)%4], c[i][0], c[i][3], c[j][(2+k)%4]);
                ll multiple6 = multi(c[j][(1+k)%4], c[i][0], c[i][3], c[j][(2+k)%4]);
                //cout << key1 << " " << key5 << ": " << key2 << " " << key3 << " "<< key4 << " " << key6 << endl;
                //cout << tileMap[key2] << " " << tileMap[key3] << " "<< tileMap[key4] << " " << tileMap[key6] << endl;
                ll tmp;
                tmp = tileMap[key2] * multiple2;  tileMap[key2]--;
                tmp *= tileMap[key3] * multiple3;  tileMap[key3]--;
                tmp *= tileMap[key4] * multiple4;  tileMap[key4]--;
                tmp *= tileMap[key6] * multiple6;
                tileMap[key2]++; tileMap[key3]++; tileMap[key4]++;
                total += tmp;
            }
            tileMap[key1]++;
            tileMap[key5]++;
        }
    }
    printf("%lli\n", total/3);

    return 0;
}

Topcoder SRM 699 Div2 review

Single Round Match 699 Sponsored by Cisco

Div II Level One: UpDownHiking

Idea is same with editorial.

class UpDownHiking {
    public:
    int maxHeight(int N, int A, int B) {
        int ans = 0;
        for (int i = 0; i <= N; ++i) {
            int tmp = min(A * i, B * (N - i));
            ans = max(ans, tmp);
        }
        return ans;
    }
};

Div II Level Two: LastDigit

Category: Arithmetric

The approach is different from editorial which uses binary search.

#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <typeinfo>
#include <fstream>

using namespace std;
typedef long long ll;

class LastDigit {
    public:
    long long findX(long long S) {
        ll base = 1111111111111111111;
        ll ans = 0;
        for (int j = 18; j >= 0; --j) {
            if (S / base > 9) {
                //cout << "fail: " << S << " " << base;
                return -1;
            }
            ans = ans * 10 + S / base;
            S = S % base;
            base /= 10;
        }
        return ans;
    }
};

Div II Level Three: FromToDivisibleDiv2

Category: Arithmetric, graph

I couldn’t solve it in contest, so I just followed editorial for my practice.

#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <typeinfo>
#include <fstream>
#include <queue>

using namespace std;

int dist[1005];

int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); }
long lcm(int a, int b) { return (long) a * b / gcd(a, b); }

class FromToDivisibleDiv2 {
    public:
    int shortest(int N, int S, int T, vector<int> a, vector<int> b) {

        unsigned long m = a.size();
        queue<int> q;
        for (int i = 0; i < m; ++i) {
            if (S % a[i] == 0) {
                dist[i]= 1;
                q.push(i);
            } else {
                dist[i] = 0; // Init dist
            }
        }

        while (!q.empty()) {
            int i = q.front();
            q.pop();
            if (T % b[i] == 0) return dist[i];
            for (int j = 0; j < m; ++j) {
                if (dist[j] == 0 && lcm(b[i], a[j]) <= N) {
                    q.push(j);
                    dist[j] = dist[i] + 1;
                }
            }
        }
        return -1;
    }
};

Codeforces Round #374 Div. 2 review

Link

A. One-dimensional Japanese Crossword

Just count consecutive ‘B’ in the sequence.

/*
 *
 */
#include <cstdio>
#include <iostream>

using namespace std;

int a[100];
char c[100];

int main(){
#ifndef ONLINE_JUDGE
    freopen("a5.in", "r", stdin);
#endif
    int n;
    scanf("%d", &n);
    scanf("%s", c);

    int k = 0;
    int count = 0;
    for (int i = 0; i < n; ++i) {
        if (c[i] == 'B') {
            count++;
        } else {
            if (count != 0) {
                a[k++] = count;
                count = 0;
            }
        }
    }
    if (count != 0) a[k++] = count;

    printf("%d\n", k);
    for (int j = 0; j < k; ++j) {
        printf("%d%c", a[j], j==k-1 ? '\n' : ' ');
    }

    return 0;
}

B. Passwords

This is also easy, just count the number for each string length. You even don’t need to process any string manipulation.

/*
 *
 */
#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

char pass[105][105];
char correct[105]; //correct pass
int count[105]; // count[i] means how many pass is length i.

int main(){
#ifndef ONLINE_JUDGE
    freopen("b2.in", "r", stdin);
#endif
    int n, k;
    scanf("%d %d", &n, &k);
    for (int i = 0; i < n; ++i) {
        scanf("%s", pass[i]);
        int len = strlen(pass[i]);
        count[len]++;
    }
    scanf("%s", correct);
    int correct_len = strlen(correct);
    int wrongpass = 0;
    for (int j = 0; j < correct_len; ++j) {
        wrongpass += count[j];
    }
    int min = wrongpass + wrongpass / k * 5 + 1;
    wrongpass += count[correct_len] - 1;
    int max = wrongpass + wrongpass / k * 5 + 1;
    printf("%d %d", min, max);

    return 0;
}

C. Journey

I felt it is difficult than problem D. During the contest, I tried to solve it by DFS but I got TLE.

/*
 * TLE
 * DFS method is not fast enough
 */
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

// road[i][j] is edge from i to start[i][j].first.
// start[i][j].second is the cost for this edge.
vector<pair<int, int> > road[5005];
int optimumroad[5005];

int n, m, T;
int maximumVisit = 0;
// current city with current time
bool dfs(int city, int time, int visited) {
    if (time > T) return false;
    if (city == n) {
        if (visited > maximumVisit) {
            maximumVisit = visited;
            return true;
        } else {
            return false;
        }
    }
    bool maximumRouteFound = false;
    for (int i = 0; i < road[city].size(); ++i) {
        if (dfs(road[city][i].first, time + road[city][i].second, visited + 1)) {
            optimumroad[city] = road[city][i].first;
            //cout << city << " " << road[city][i].first << endl;
            maximumRouteFound = true;
        }

    }
    return maximumRouteFound;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("c4.in", "r", stdin);
#endif
    scanf("%d %d %d", &n, &m, &T);

    for (int i = 0; i < m; ++i) {
        int u, v, t;
        scanf("%d %d %d", &u, &v, &t);
        road[u].push_back(make_pair(v, t));
    }

    dfs(1, 0, 1);
    printf("%d\n", maximumVisit);
    int currentcity = 1;
    printf("1 ");
    while (true) {
        currentcity = optimumroad[currentcity];
        if (currentcity == n) {
            printf("%d\n", n);
            break;
        } else {
            printf("%d ", currentcity);
        }
    }
    return 0;
}

To get answer, we can use DP (dynamic programming) to reduce computational complexity as \(O(mn)\).

/*
 * DP method
 * Accepted	93 ms	98300 KB
 */
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

#define INF (1e9+5)

struct edge {
    int from;
    int to;
    int cost;
};

vector<edge> roadvec;
int optimumroad[5005];

int n, m, T;
int maximumVisit = 0;


// dp[i][j]: Minimum cost when already visited i cities, for city j.
// INF indicates not reachable.
// Do not use 0 for i and j.
int dp[2][5005];

// to remember optimal path. path[i][j]: To visit i cities at city j, optimal path is come from path[i][j].
int path[5005][5005];

int main(){
#ifndef ONLINE_JUDGE
    freopen("c4.in", "r", stdin);
#endif
    scanf("%d %d %d", &n, &m, &T);

    for (int i = 0; i < m; ++i) {
        int u, v, t;
        scanf("%d %d %d", &u, &v, &t);
        roadvec.push_back(edge{u, v, t});
    }


    for (int j = 0; j < 5005; ++j) {
        dp[0][j] = INF;
    }
    dp[0][1] = 0;

    for (int i = 1; i < n; ++i) {
        for (int j = 0; j < 5005; ++j) {
            dp[1][j] = INF;  // Need to init each time!
        }
        for (int j = 0; j < m; ++j) {
            if (dp[0][roadvec[j].from] + roadvec[j].cost < dp[1][roadvec[j].to]) {
                dp[1][roadvec[j].to] = dp[0][roadvec[j].from] + roadvec[j].cost;
                path[i+1][roadvec[j].to] = roadvec[j].from;
                //cout << "i " << i << ", from " << roadvec[j].from <<
                //        ", to " << roadvec[j].to << ", cost " << roadvec[j].cost <<
                //        ", dp[1][to] " << dp[1][roadvec[j].to] << endl;
            }
        }
        if (dp[1][n] <= T) {
            maximumVisit = i+1;
        }
        swap(dp[0], dp[1]);
    }
    // Reconstruct optimum road path
    optimumroad[maximumVisit] = n;
    for (int i = maximumVisit - 1; i > 0; --i) {
        optimumroad[i] = path[i+1][optimumroad[i+1]];
    }

    printf("%d\n", maximumVisit);
    for (int k = 1; k <= maximumVisit; ++k) {
        printf("%d%c", optimumroad[k], k==maximumVisit ? '\n' : ' ');
    }
    return 0;
}

D. Maxim and Array

Basic strategy to get minimum product is as follows,

1. If the product is positive, then we want to reduce it. We can change the sign of number whose absolute value is minimum.

2. If the product is 0, then there exists at least one 0 in the array. We can change this 0 to get total product as negative.

3. If the product is negative, then we want to keep the sign of product as negative and just want to get the total absolute value product as maximum. To do this, we should get minimum absolute value and change this absolute value by +x.

To get minimum absolute value for each iteration efficiently, we can use priority_queue.

/*
 * Accepted	233 ms	9300 KB
 * use priority_queue
 */
#include <cstdio>
#include <iostream>
#include <queue>


using namespace std;

typedef long long ll;
typedef pair<ll, ll> P;

ll sign[200005]; // sign: +1 positive or 0, -1 negative.
ll ans[200005];

ll checksign(ll a) {
    if (a >= 0) {
        return 1;
    } else {
        return -1;
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("d5.in", "r", stdin);
#endif
    ll n, k, x;
    scanf("%lli %lli %lli", &n, &k, &x);

    priority_queue<P, vector<P>, greater<P> > q;
    ll negative_count = 0;
    ll a;
    for (int i = 0; i < n; ++i) {
        scanf("%lli", &a);
        sign[i] = checksign(a);
        if (sign[i] == -1) negative_count++;
        q.push(make_pair(a * sign[i], i));
    }

    P current = q.top();
    ll min = current.first;
    if (negative_count % 2 == 0 && min >= k * x) { // positive case: no 0 exists and we cannot make total product as 0 or negative
        q.pop();
        q.push(make_pair(current.first - k * x, current.second));
    } else {
        if (negative_count % 2  == 0) {  // Make negative_count as odd
            ll repeat = min / x + 1;
            q.pop();
            q.push(make_pair(-(current.first - repeat * x), current.second));
            k -= repeat;
            if (sign[current.second] == 1) {
                sign[current.second] = -1;
                negative_count++;
            } else {
                sign[current.second] = 1;
                negative_count--;
            }
        }
        while (k > 0) {
            current = q.top(); q.pop();
            q.push(make_pair(current.first + x, current.second));
            k--;
        }
    }

    while (!q.empty()) {
        P current = q.top(); q.pop();
        ans[current.second] = sign[current.second] * current.first;
    }

    for (int i = 0; i < n; ++i) {
        printf("%lli%c", ans[i], i==n-1 ? '\n' : ' ');
    }
    return 0;
}

Codeforces Round #373 Div. 2 review

Link

A. Vitya in the Countryside

Basically, check last 2 character to determine its direction is “UP” or “DOWN”. Special case comes when the last number is 0 or 15, in that case the direction changes and you can determine the direction without looking second last character.

#include <cstdio>
#include <iostream>

using namespace std;

int a[100];

int main(){
#ifndef ONLINE_JUDGE
    freopen("a3.in", "r", stdin);
#endif
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &a[i]);
    }
    if (n >= 2) {
        if (a[n-1] == 15) {
            printf("DOWN\n");
        } else if (a[n-1] == 0) {
            printf("UP\n");
        } else {
            if (a[n-1] > a[n-2]) {
                printf("UP\n");
            } else {
                printf("DOWN\n");
            }
        }
    } else if (n == 1) {
        if (a[n-1] == 15) {
            printf("DOWN\n");
        } else if (a[n-1] == 0) {
            printf("UP\n");
        } else {
            printf("-1\n");
        }
    }
    return 0;
}

B. Anatoly and Cockroaches

We can consider 2 patterns for final alignment. Pattern 0 is to align “rbrbrb…” and pattern 1 is to align “brbrbr…”.

We will compare the original alignment and final alignment. For each position i, we need to change the color to…

  • Case A: r to b
  • Case B: b to r

If both case A and B exists, the optimal way to change color is to replace these cockroaches. and if only case A or case B exists we can change color by can. After this optimal way, the total number of count is same the maximum number of Case A & Case B.

We take smaller number of total count between pattern 0 & 1.

/*
 * Accepted	46 ms	244 KB
 */
#include <cstdio>
#include <iostream>

using namespace std;


int main(){
#ifndef ONLINE_JUDGE
    freopen("b.in", "r", stdin);
#endif
    int n;
    scanf("%d", &n);

    string s;
    cin >> s;
    // 0: rbrbrb..., 1 brbrbr...
    int rtob[2] = {0};
    int btor[2] = {0};

    // 0
    for (int i = 0; i < n; ++i) {
        char target0 = (i%2) ? 'r' : 'b';
        if (s[i] == target0) {
            //i.e., s[i] != target1
            if (i%2==0) {
                rtob[1]++;
            } else {
                btor[1]++;
            }
        } else {
            if (i%2==0) {
                btor[0]++;
            } else {
                rtob[0]++;
            }
        }
    }

    int count[2];
    count[0] = max(rtob[0], btor[0]);
    count[1] = max(rtob[1], btor[1]);

    int ans = min(count[0], count[1]);
    printf("%d\n", ans);

    return 0;
}

C. Efim and Strange Grade

The best policy to update is scan from the nearest integer and if the number is greater than or equal to 5, then round it.

Special case heppens only when the rounded value changes from 4 to 5, then we need to round this value.

So if we have ….444445…., we need to update manytimes. But if you have ….24244336…, rounding 6 one time is enough.

/*
 * Accepted	77 ms	500 KB
 */
#include <cstdio>
#include <iostream>

using namespace std;

int main(){
#ifndef ONLINE_JUDGE
    freopen("c7.in", "r", stdin);
#endif
    int n, t;
    scanf("%d %d", &n, &t);

    string s;
    cin >> s;

    int decimalPoint = -1;
    for (int i = 0; i < n; ++i) {
        if (s[i] == '.') {
            decimalPoint = i;
            break;
        }
    }

    if (decimalPoint == -1) {
        // do nothing
    } else {
        int left = -1;  // position of most left 4 of ...444445...
        int right = -1; // position of right 5 of ...444445...
        bool reset = true;
        for (int i = decimalPoint + 1; i < n; ++i) {
            if (s[i] < '4') {
                // reset flag
                reset = true;
                left = -1;
            } else if (s[i] == '4') {
                if (reset) {
                    left = i;
                    reset = false;
                } else {
                    // do nothing
                }
            } else {  // s[i] >= '5'
                right = i;
                break;
            }
        }
        if (right == -1) {
            // nothing should be rounded
        } else {
            if (left == -1) {
                // round up only once at position 'right'
                if (right == decimalPoint + 1) {
                    // increment up integer

                    int checkPos = decimalPoint-1;
                    while (checkPos >= 0) {
                        if (s[checkPos] == '9') {
                            s[checkPos] = '0';
                            checkPos--;
                        } else {
                            s[checkPos]++;
                            break;
                        }
                    }
                    s = s.substr(0, decimalPoint);
                    if (checkPos < 0) {
                        s = "1" + s;
                    }
                } else {
                    s[right-1]++;
                    s = s.substr(0, right);
                }
            } else { // left != -1
                // form of ...4445...
                if (t < right-left+1) {
                    // round up t times is the optimal way
                    s[right - t] = '5';
                    s = s.substr(0, right - t + 1);
                } else {
                    // round up only once at position 'left' == 5
                    if (left == decimalPoint + 1) {
                        // increment up integer

                        int checkPos = decimalPoint-1;
                        while (checkPos >= 0) {
                            if (s[checkPos] == '9') {
                                s[checkPos] = '0';
                                checkPos--;
                            } else {
                                s[checkPos]++;
                                break;
                            }
                        }
                        s = s.substr(0, decimalPoint);
                        if (checkPos < 0) {
                            s = "1" + s;
                        }
                    } else {
                        s[left-1]++;
                        s = s.substr(0, left);
                    }
                }
            }

        }
    }
    cout << s;

    return 0;
}

Codeforces Round #368 Div. 2 review

Link

A. Brain’s Photos

Just need to check if color (Cyan, Magenta or Yellow) exists or not in the photo.

#include <cstdio>
#include <iostream>

using namespace std;

int main(){
#ifndef ONLINE_JUDGE
    freopen("a3.in", "r", stdin);
#endif
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            char c;
            cin >> c;
            if (c == 'C' || c == 'M' || c == 'Y') {
                cout << "#Color" << endl;
                return 0;
            }
        }
    }
    cout << "#Black&White" << endl;
    return 0;
}

B. Bakery

The problem sentence looks long, but after summarize, you just need to find the path between flour storage city and non flour storage city with minimum distance.

But to find out these city path, I was using vector which is not fast enough and I got TLE during contest.

/*
 *
 */
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>

#define INF (1e9+5)
using namespace std;

int u[100005];
int v[100005];
int l[100005];
vector<int> s;

// ---> TLE, find takes O(N) time, too long.
bool contain(vector<int> s, int a) {
    return find(s.begin(), s.end(), a) != s.end();
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("b3.in", "r", stdin);
#endif
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 0; i < m; ++i) {
        cin >> u[i] >> v[i] >> l[i];
    }
    for (int i = 0; i < k; ++i) {
        int si;
        cin >> si;
        s.push_back(si);
    }

    int ans = INF;
    for (int i = 0; i < m; ++i) {
        if ((contain(s, u[i]) && !contain(s, v[i]))
            || (!contain(s, u[i]) && contain(s, v[i]))){
            ans = min(ans, l[i]);
        }
    }

    if (ans == INF) {
        cout << -1 << endl;
    } else {
        cout << ans << endl;
    }
    return 0;
}

We can make search fast, in constant time by using Array to set a type of city.

/*
 * Previously, citytype was checked by vector, it takes O(n) to check i-th city type.
 * Now, use citytype Array to check the city type. this is O(1).
 */
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>

#define INF (1e9+5)
using namespace std;

int u[100005];
int v[100005];
int l[100005];
int citytype[100005];  // 0: no storage, 1: storage

int main(){
#ifndef ONLINE_JUDGE
    freopen("b.in", "r", stdin);
#endif
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 0; i < m; ++i) {
        cin >> u[i] >> v[i] >> l[i];
    }
    for (int i = 0; i < k; ++i) {
        int si;
        cin >> si;
        citytype[si] = 1;
    }

    int ans = INF;
    for (int i = 0; i < m; ++i) {
        if (citytype[u[i]] != citytype[v[i]]){ // <--- Use array, O(1).
            ans = min(ans, l[i]);
        }
    }

    if (ans == INF) {
        cout << -1 << endl;
    } else {
        cout << ans << endl;
    }
    return 0;
}

C. Pythagorean Triples

Consider some cases to get answer.

If n>=3, there exists answer. And we can show one representative answer.

/*
 *
 */
#include <cstdio>
#include <iostream>

typedef long long ll;
using namespace std;

int main(){
#ifndef ONLINE_JUDGE
    freopen("c.in", "r", stdin);
#endif
    ll n;
    cin >> n;
    if (n==1 || n==2) {
        cout << -1 << endl;
        return 0;
    } else {
        ll scale = 1;

        while ((n&1)==0){
            // even
            if (n==4) {
                cout << 3 * scale << " " << 5 * scale << endl;
                return 0;
            }

            n >>= 1;
            scale <<= 1;
        }
        // odd
        ll b, c;
        ll n2 = (n * n);
        b = (n2 - 1) / 2;
        c = (n2 + 1) / 2;
        cout << b * scale << " " << c * scale << endl;
    }
    return 0;
}

D. Persistent Bookcase

  • DFS with forward&backward operation
  • Problem

This problem focuses how to make persistent data structures. I imagine the data management of git (branch) also works in similar way.

We cannot have all the time of book shelf state in the memory, so how to achive the operation 4 is the problem. We can do it by using Depth First Search, and after dfs we need to “rollback” the operation to get previous shelf state.

/*
 * Ref: http://codeforces.com/submissions/ksun48 707D
 * Use DFS
 * forward & backward
 *
 * Use scanf, printf instead of cin, cout
 * d2.cpp cin & cout:     717 ms	11500 KB
 * d4.cpp scanf & printf: 171 ms	11500 KB --> The difference is super huge. Use scanf & printf.
 */
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

int n, m;
int a[100005], b[100005], c[100005];
int ans[100005];

bool shelf[1005][1005];
int count_shelf[1005];
int total_count = 0;

vector<int> children[100005]; // children[i] : child of i-th operation = next operation

void dfs(int q) { // now at q-th operation
    ans[q] = total_count;
    //cout << "debug q = " << q << ", ans[q] = " << ans[q] << endl;

    for (int i = 0; i < children[q].size(); ++i) {
        int curq = children[q][i];

        /* Forward, Apply q-th operation */
        bool changed = false;
        switch (a[curq]) {
            case 1:
                if (!shelf[b[curq]][c[curq]]) {
                    count_shelf[b[curq]]++;
                    total_count++;
                    shelf[b[curq]][c[curq]] = true;
                    changed = true;
                }
                break;
            case 2:
                if (shelf[b[curq]][c[curq]]) {
                    count_shelf[b[curq]]--;
                    total_count--;
                    shelf[b[curq]][c[curq]] = false;
                    changed = true;
                }
                break;
            case 3:
                for (int j = 0; j < m; ++j) {
                    shelf[b[curq]][j] = !shelf[b[curq]][j];
                }
                total_count += m - 2 * count_shelf[b[curq]];
                count_shelf[b[curq]] = m - count_shelf[b[curq]];
                break;
            case 4:
                // jumped from q to curq, do nothing
                break;
        }

        dfs(curq);

        /* Backward Rollback q-th operation.
         * changed flag, set at forward operation, is used.  */
        switch (a[curq]) {
            case 1:
                if (changed) {
                    count_shelf[b[curq]]--;
                    total_count--;
                    shelf[b[curq]][c[curq]] = false;
                }
                break;
            case 2:
                if (changed) {
                    count_shelf[b[curq]]++;
                    total_count++;
                    shelf[b[curq]][c[curq]] = true;
                }
                break;
            case 3:
                for (int j = 0; j < m; ++j) {
                    shelf[b[curq]][j] = !shelf[b[curq]][j];
                }
                total_count += m - 2 * count_shelf[b[curq]];
                count_shelf[b[curq]] = m - count_shelf[b[curq]];
                break;
            case 4:
                // do nothing and go back from curq to q
                break;
        }
        //cout << "debug after rollback q = " << q << ", ans[q] = " << ans[q] << endl;
    }
}


int main(){
#ifndef ONLINE_JUDGE
    freopen("d4.in", "r", stdin);
#endif
    //vector<int> v;
    int q;
    scanf("%d %d %d", &n, &m, &q);
    for (int i = 1; i <= q; ++i) {
        scanf("%d", &a[i]);
        if (a[i]==1 || a[i]==2) {
            scanf("%d %d", &b[i], &c[i]);
            b[i]--;
            c[i]--;
        } else {
            scanf("%d", &b[i]);
            if (a[i]==3) {
                b[i]--;
            }
        }

        /* Create DFS tree */
        if (a[i]==4) {
            // i-th operation is 4
            children[b[i]].push_back(i);
        } else {
            children[i-1].push_back(i);
        }
    }

    dfs(0);

    for (int i = 1; i <= q; ++i) {
        printf("%d\n", ans[i]);
        //cout << ans[i] << endl;
    }
    return 0;
}

Here I decided to use C scanf() and printf() function instead of C++ cin/cout function. Because I noticed there is quite a big difference in the speed. See below post for detail.

scanf and printf are much faster than cin and cout

When I’m solving the Codeforces problem, 707D. Persistent Bookcase, I made some comparison for the performance of I/O function.

  • scanf() and printf()
  • cin >> and cout <<

Test

Let’s start from C++ common way, below code uses cin >> and cout <<

/*
 * Ref: http://codeforces.com/submissions/ksun48 707D
 * Use DFS
 * forward & backward
 *
 * Accepted	748 ms	11500 KB
 */
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

int n, m;
int a[100005], b[100005], c[100005];
int ans[100005];

bool shelf[1005][1005];
int count_shelf[1005];
int total_count = 0;

vector<int> children[100005]; // children[i] : child of i-th operation = next operation

void dfs(int q) { // now at q-th operation
    ans[q] = total_count;
    //cout << "debug q = " << q << ", ans[q] = " << ans[q] << endl;

    for (int i = 0; i < children[q].size(); ++i) {
        int curq = children[q][i];

        /* Forward, Apply q-th operation */
        bool changed = false;
        switch (a[curq]) {
            case 1:
                if (!shelf[b[curq]][c[curq]]) {
                    count_shelf[b[curq]]++;
                    total_count++;
                    shelf[b[curq]][c[curq]] = true;
                    changed = true;
                }
                break;
            case 2:
                if (shelf[b[curq]][c[curq]]) {
                    count_shelf[b[curq]]--;
                    total_count--;
                    shelf[b[curq]][c[curq]] = false;
                    changed = true;
                }
                break;
            case 3:
                for (int j = 0; j < m; ++j) {
                    shelf[b[curq]][j] = !shelf[b[curq]][j];
                }
                total_count += m - 2 * count_shelf[b[curq]];
                count_shelf[b[curq]] = m - count_shelf[b[curq]];
                break;
            case 4:
                // jumped from q to curq, do nothing
                break;
        }

        dfs(curq);

        /* Backward Rollback q-th operation.
         * changed flag, set at forward operation, is used.  */
        switch (a[curq]) {
            case 1:
                if (changed) {
                    count_shelf[b[curq]]--;
                    total_count--;
                    shelf[b[curq]][c[curq]] = false;
                }
                break;
            case 2:
                if (changed) {
                    count_shelf[b[curq]]++;
                    total_count++;
                    shelf[b[curq]][c[curq]] = true;
                }
                break;
            case 3:
                for (int j = 0; j < m; ++j) {
                    shelf[b[curq]][j] = !shelf[b[curq]][j];
                }
                total_count += m - 2 * count_shelf[b[curq]];
                count_shelf[b[curq]] = m - count_shelf[b[curq]];
                break;
            case 4:
                // do nothing and go back from curq to q
                break;
        }
        //cout << "debug after rollback q = " << q << ", ans[q] = " << ans[q] << endl;
    }
}


int main(){
#ifndef ONLINE_JUDGE
    freopen("d4.in", "r", stdin);
#endif
    //vector<int> v;
    int q;
    cin >> n >> m >> q;
    for (int i = 1; i <= q; ++i) {
        cin >> a[i];
        if (a[i]==1 || a[i]==2) {
            cin >> b[i] >> c[i];
            b[i]--;
            c[i]--;
        } else {
            cin >> b[i];
            if (a[i]==3) {
                b[i]--;
            }
        }

        /* Create DFS tree */
        if (a[i]==4) {
            // i-th operation is 4
            children[b[i]].push_back(i);
        } else {
            children[i-1].push_back(i);
        }
    }

    dfs(0);

    for (int i = 1; i <= q; ++i) {
        cout << ans[i] << endl;
    }
    return 0;
}

It took 748 ms.

Next, (old-school,) C way of implementation using scanf() and printf()

/*
 * Ref: http://codeforces.com/submissions/ksun48 707D
 * Use DFS
 * forward & backward
 *
 * Use scanf, printf instead of cin, cout
 * scanf & printf: 171 ms	11500 KB --> The difference is super huge. Use scanf & printf.
 */
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

int n, m;
int a[100005], b[100005], c[100005];
int ans[100005];

bool shelf[1005][1005];
int count_shelf[1005];
int total_count = 0;

vector<int> children[100005]; // children[i] : child of i-th operation = next operation

void dfs(int q) { // now at q-th operation
    ans[q] = total_count;
    //cout << "debug q = " << q << ", ans[q] = " << ans[q] << endl;

    for (int i = 0; i < children[q].size(); ++i) {
        int curq = children[q][i];

        /* Forward, Apply q-th operation */
        bool changed = false;
        switch (a[curq]) {
            case 1:
                if (!shelf[b[curq]][c[curq]]) {
                    count_shelf[b[curq]]++;
                    total_count++;
                    shelf[b[curq]][c[curq]] = true;
                    changed = true;
                }
                break;
            case 2:
                if (shelf[b[curq]][c[curq]]) {
                    count_shelf[b[curq]]--;
                    total_count--;
                    shelf[b[curq]][c[curq]] = false;
                    changed = true;
                }
                break;
            case 3:
                for (int j = 0; j < m; ++j) {
                    shelf[b[curq]][j] = !shelf[b[curq]][j];
                }
                total_count += m - 2 * count_shelf[b[curq]];
                count_shelf[b[curq]] = m - count_shelf[b[curq]];
                break;
            case 4:
                // jumped from q to curq, do nothing
                break;
        }

        dfs(curq);

        /* Backward Rollback q-th operation.
         * changed flag, set at forward operation, is used.  */
        switch (a[curq]) {
            case 1:
                if (changed) {
                    count_shelf[b[curq]]--;
                    total_count--;
                    shelf[b[curq]][c[curq]] = false;
                }
                break;
            case 2:
                if (changed) {
                    count_shelf[b[curq]]++;
                    total_count++;
                    shelf[b[curq]][c[curq]] = true;
                }
                break;
            case 3:
                for (int j = 0; j < m; ++j) {
                    shelf[b[curq]][j] = !shelf[b[curq]][j];
                }
                total_count += m - 2 * count_shelf[b[curq]];
                count_shelf[b[curq]] = m - count_shelf[b[curq]];
                break;
            case 4:
                // do nothing and go back from curq to q
                break;
        }
        //cout << "debug after rollback q = " << q << ", ans[q] = " << ans[q] << endl;
    }
}


int main(){
#ifndef ONLINE_JUDGE
    freopen("d4.in", "r", stdin);
#endif
    //vector<int> v;
    int q;
    scanf("%d %d %d", &n, &m, &q);
    for (int i = 1; i <= q; ++i) {
        scanf("%d", &a[i]);
        if (a[i]==1 || a[i]==2) {
            scanf("%d %d", &b[i], &c[i]);
            b[i]--;
            c[i]--;
        } else {
            scanf("%d", &b[i]);
            if (a[i]==3) {
                b[i]--;
            }
        }

        /* Create DFS tree */
        if (a[i]==4) {
            // i-th operation is 4
            children[b[i]].push_back(i);
        } else {
            children[i-1].push_back(i);
        }
    }

    dfs(0);

    for (int i = 1; i <= q; ++i) {
        printf("%d\n", ans[i]);
        //cout << ans[i] << endl;
    }
    return 0;
}

It took only 171 ms.

Result

functiontime
scanf() and printf()171 ms
cin >> and cout <<748 ms

In this experiment, it appears that the speed of cin and cout is much much slower than scanf() and printf()the difference is indeed quite huge.

The overhead of cin and cout took more than 500 ms! You might get a TLE using cin and cout while it is not necessary with scanf() and printf().

Conclusion: Use scanf and printf instead of cin and cout in programming contest!

AtCoder Regular Contest 060 review

Link

C: Tak and Cards – 高橋君とカード

  • Dynamic programming

I could come up only full search using bit operation during the contest.

But it can be solved by DP. Below is full score (Section 3.3 of editorial) implementation.

/*
 * Ref http://arc060.contest.atcoder.jp/data/arc/060/editorial.pdf
 * 3.3 Full score solution
 *
 * DP
 */
#include <cstdio>
#include <iostream>

typedef long long ll;
using namespace std;

ll x[51];

// dp_j[t] : possible #of sum = t - n*X using card y[i] (0<=i<=j) where y[i] := x[i] - a
ll dp[2][2*51*51];

int main(){
    //freopen("c4.in", "r", stdin);
    ll n, a;
    ll X = 0;  // Maximum absolute value of y[i], up to 50

    cin >> n >> a;

    for (ll i = 0; i < n; ++i) {
        cin >> x[i];
        x[i] -= a;
        X = max(X, abs(x[i]));
    }

    // Init
    dp[0][n*X] = 1LL;
    for (int j = 0; j < n; ++j) {
        for (ll t = 2*n*X; t >= 0; --t) {
            ll prevt = t-x[j];
            if (0 <= prevt && prevt <= 2*n*X) {
                dp[1][t] = dp[0][t] + dp[0][prevt];
            }
        }
        swap(dp[0], dp[1]);
    }

    cout << dp[0][n*X] - 1 << endl;
    return 0;
}

D: Digit Sum – 桁和

I came up different approach from editorial.

Let ai be i-th digit with base b. Then,

$$ n = \sum_i b^i a_i $$ $$ s = f(b, n) = \sum_i a_i $$

applies. If we subtract these equation, we obtain

$$ n – s = \sum_i (b^i – 1) a_i $$

Both side should be integer, and right hand side can be divided by \( (b−1) \). So \(b−1\) is a divisor of \(n−s\). To find out divisor of \(n−s\), it is enough to scan from 1 to \(\sqrt{n−s}\). The computational complexity is thus \(O(\sqrt{n−s})\)

/*
 *
 */
#include <cstdio>
#include <iostream>
#include <cmath>

#define INF ((ll)1e11 + 5)
typedef long long ll;
using namespace std;

// check whether f(b, n) == s exists or not.
bool check(ll b, ll n, ll s) {
    ll fbn = 0;
    while (n != 0) {
        fbn += n % b;
        n /= b;
    }
    return  fbn == s;
}

int main(){
    //freopen("d5.in", "r", stdin);
    ll n, s;
    cin >> n >> s;

    ll d = n - s;
    if (d < 0) {
        cout << -1 << endl;
    } else if (d == 0) {
        cout << n+1 << endl;
    } else { // n > s
        ll minB = INF;
        ll ub = (ll)sqrt((double) d) + 1LL;
        for (ll i = 1; i <= ub; ++i) {
            if (d % i == 0) {
                if (check(i + 1, n, s)) minB = min(minB, i + 1);
                if (check(d / i + 1, n, s)) minB = min(minB, d / i + 1);
            }
        }
        if (minB == INF) {
            cout << -1 << endl;
        } else {
            cout << minB << endl;
        }
    }
    return 0;
}

E: Tak and Hotels – 高橋君とホテル

During the contest, I was writing primitive solution. Its computational complexity is \(O(NQ)\) and thus it can answer only small size problem 〜 \(10^3 ∗ 10^3 = 10^6 \).
/*
 * Improve search to use upper_bound()
 * Computational Complexity O(NQ)  --- not change from e1.cpp
 */
#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

int x[100005];
vector<pair<int, int>> vec; //  x_i, city

int main(){
    //freopen("e.in", "r", stdin);
    int n;
    int L;
    int Q;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> x[i];
        vec.push_back(make_pair(x[i], i+1));
    }
    cin >> L;
    cin >> Q;

    sort(vec.begin(), vec.end());
    sort(x, x+n);

    for (int j = 0; j < Q; ++j) {
        int a, b;
        int ai, bi;
        cin >> a >> b;
        for (int i = 0; i < n; ++i) {
            if (vec[i].second == a) ai = i;
            if (vec[i].second == b) bi = i;
        }

        if (ai > bi) { swap(ai, bi); }

        ll day = 0;
        while (ai < bi) {
            int* xub = upper_bound(x, x+n, x[ai]+L);
            ai = xub - x - 1;
            day++;
        }
        cout << day << endl;
    }

    return 0;
}

 To solve big size solution, you beed a pre-calculation to prepare table r[k][i], explained in editorial.

Sample implementation is below.

/*
 * Ref: http://arc060.contest.atcoder.jp/data/arc/060/editorial.pdf
 *
 * LCA (Lowest Common Ancestor), Doubling
 * O((N+Q)log N)
 */
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>

using namespace std;

typedef long long ll;

int x[100005];
map<int, int> city_x_map; //  city, x_i

int r[18][100005]; // r[k][i]: hotel number, which we can reach from i-th hotel within 2^k days. (2^17 > 100005)

// assuming a <= b
ll calcDay(int a, int b) {
    ll day = 0;
    while (true) {
        if (a == b) { return day; }

        int k = 0;
        while(r[k][a] < b) {
            k++;
            //cout << "k " << k << ", a " << a << ", r[k][a] " << r[k][a] << endl;
        }
        // now r[k][a] >= b, update a and day
        if (k == 0) {
            return day+1LL;
        } else {
            a = r[k-1][a];
            day += (1LL << (k - 1));
        }
    }
}

int main(){
    //freopen("e.in", "r", stdin);
    int n;
    int L;
    int Q;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> x[i];
        city_x_map.insert(make_pair(i+1, x[i]));
    }
    cin >> L;
    cin >> Q;

    sort(x, x+n);

    // Prepare r[k][i] using doubling
    for (int i = 0; i < n; ++i) {
        r[0][i] = (int)(upper_bound(x, x+n, x[i]+L) - x) - 1;
    }
    int maxK = 0;
    while (n >> maxK > 0) maxK++;
    for (int k = 0; k < maxK; ++k) {
        for (int i = 0; i < n; ++i) {
            r[k+1][i] = r[k][r[k][i]];
        }
    }

    // solve
    for (int j = 0; j < Q; ++j) {
        int a, b;     // original hotel number
        int axi, bxi; // hotel x coordinate
        cin >> a >> b;
        axi = city_x_map[a];
        bxi = city_x_map[b];

        if (axi > bxi) { swap(axi, bxi); }

        a = lower_bound(x, x+n, axi) - x;  // sorted hotel number
        b = lower_bound(x, x+n, bxi) - x;  // sorted hotel number

        //cout << a << " " << b << endl; // debug

        ll day = calcDay(a, b);

        cout << day << endl;
    }
    return 0;
}