Submission #159928

#TimeUsernameProblemLanguageResultExecution timeMemory
159928rama_pangGame (IOI14_game)C++14
100 / 100
460 ms16160 KiB
#include "game.h"
#include <bits/stdc++.h>
using namespace std;

/*  The ideal strategy is to keep saying no unless it is the last
    edge such that if we say no, then the graph becomes disconnected.

    Assume that the graph is partially filled, such that there are two
    components now. How to check if the guessed edge is the last edge?
    The number of possible edges between the two components are
    sizeA * sizeB. If cur_edge == sizeA * sizeB, then we can add an edge.

    Observe that the optimal strategy for the asker is to ask questions
    on two disjoint vertices. All already connected vertices are meaningless.
    By denying edges, we ensure that the asker would need all Q = N * (N - 1) / 2
    questions.

    Proof: assume that there are n disjoint components. Let A, B, C, D be sets
    with sizeA <= sizeB <= sizeC <= sizeD. What is the optimal strategy of
    minimum questions needed? There are a few cases:
    1.  sizeA * sizeB + sizeC * sizeD + (sizeA + sizeB) * (sizeC + sizeD) total questions.
        = sizeA * sizeB + sizeA * sizeC + sizeA * sizeD + sizeB * sizeC + sizeB * sizeD
          + sizeC * sizeD

    2.  sizeA * sizeB + (sizeA + sizeB) * sizeC + (sizeA + sizeB + sizeC) * sizeD
        = sizeA * sizeB + sizeA * sizeC + sizeA * sizeD + sizeB * sizeC + sizeB * sizeD
          + sizeC * sizeD

    for any permutation of A, B, C, and D. Thus we can see that for any strategy, if we
    give the asker an edge at the last possible edge between two currently disjoint 
    components, then asker would require the same number of questions.
    The total questions needed is: 1 * 1 + 2 * 1 + 3 * 1 + ... = N * (N - 1) / 2.
    So the strategy of waiting until last edge of two currently disjoint components is an
    optimal strategy.

    We can keep track of components' size with disjoint set data structure and countEdges
    with adjacency matrix.

*/

struct Disj {
    vector<int> p, sz;
    
    void init(int n) {
        p.resize(n);
        iota(p.begin(), p.end(), 0);
        sz.assign(n, 1);
    }

    int par(int n) {
        return p[n] == n ? n : p[n] = par(p[n]);
    }

    bool join(int a, int b) {
        a = par(a), b = par(b);
        if (a == b) return false;
        p[a] = b;
        sz[b] += sz[a];
        sz[a] = 0;
        return true;
    }

};

int N;
vector<vector<int>> cnt;
Disj dsu;

void initialize(int n) {
    N = n;
    cnt.assign(n, vector<int>(n, 0));
    dsu.init(n);
}

int hasEdge(int u, int v) {
    u = dsu.par(u), v = dsu.par(v);
    if (u > v) swap(u, v);
    if (++cnt[u][v] == dsu.sz[u] * dsu.sz[v]) {
        dsu.join(u, v);
        for (int i = 0; i < N; i++) cnt[min(v, i)][max(v, i)] += cnt[min(u, i)][max(u, i)];
        return 1;
    } else {
        return 0;
    }
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...