KIC 003 解説

総評

antaさん全完おめでとうございます!(47:54)
tanakhさん全完おめでとうございます!(102:34)
全完が2人しか居ないのは正直予想外でした。が、問題ごとの正解者数についてはおおむね予想通りです。

A問題 (FA:kyuridenamidaさん 02:08)

CDに、曲を長さが短い順に入れていけばOKです
入出力例2はwac「音楽」で3はラブライブ!のベストアルバムです

B問題 (FA:yuustiさん 06:03)

想定解法は、トレーニングする体力があるなら出来る限りトレーニングするGreedyです
O(NH)のDPでも間に合います。
本当はDPに行った人に時間を掛けさせるためにDPも通る制約にしたのに、antaさんがDPで爆速で通していて想定外です。
体力をHより多くまで回復させてる人が多かったですね。

C問題 (FA:kyuridenamidaさん 12:09)

長さ4以上のサイクルみたいなのがあれば逃げ切れる…?みたいなのを考えるとハマると思います。
盤面の状態数は、aの位置bの位置共にN通りなのでN^2です、なので実はN^2ターン逃げたら逃げ切った判定でOKです。
それを用いるとO(N^5)のDPで答えが求まります。
dp[aの位置][bの位置][ターン数]ですね

D問題 (FA:Mi_sawaさん 21:07)

ax=bなる素数xが存在したらaとbに辺を張るグラフを作ります。
それのグラフで考えると、最大独立集合を求めれば良い事に気づきます。
aとbは素因数の個数の偶奇が違うので、二部グラフになっています。
二部グラフの最大独立集合は最大マッチングから出せます。
なお、集合の中に1がある場合には注意してください。{1,2,4}の答えは2です({1,4})。


コード

C++11で、includeは省いています。

A

using namespace std;

typedef long long ll;
typedef pair<int, int> P;
typedef tuple<int, int, int> T;

int main() {
    int n;
    cin >> n;
    vector<int> v;
    for (int i = 0; i < n; i++) {
        int x, y;
        cin >> x >> y;
        v.push_back(x*60+y+3);
    }
    sort(v.begin(), v.end());
    int sum = 0;
    int i;
    for (i = 0; i < n; i++) {
        if (sum + v[i] > 80*60+3) break;
        sum += v[i];
    }
    cout << i << endl;
    return 0;
}

B

using namespace std;

typedef long long ll;
typedef pair<int, int> P;
typedef tuple<int, int, int> T;

int main() {
    ll N;
    cin >> N;
    ll H;
    cin >> H;
    ll A, X, B, Y;
    cin >> A >> X;
    cin >> B >> Y;
    ll k = 0, t = H;
    for (int i = 0; i < N; i++) {
        if (t >= X) {
            t -= X;
            k += A;
        } else {
            t = min(H, t+Y);
            k -= B;
        }
    }
    cout << k << endl;
    return 0;
}

C

using namespace std;

typedef long long ll;
typedef pair<int, int> P;
typedef tuple<int, int, int> T;
const int MN = 40;
bool dp1[MN][MN][MN*MN];
bool dp2[MN][MN][MN*MN];
bool used1[MN][MN][MN*MN];
bool used2[MN][MN][MN*MN];

int n;
vector<int> g[MN];
bool solve1(int x, int y, int t); //turn A
bool solve2(int x, int y, int t); //turn B

//A win:true
bool solve1(int x, int y, int t) {
    if (x == y) return false;
    if (t < 0) return true;
    if (used1[x][y][t]) return dp1[x][y][t];
    bool f = !solve2(x, y, t);
    for (int d: g[x]) {
        if (!solve2(d, y, t)) {
            f = true;
            break;
        }
    }
    used1[x][y][t] = true;
    dp1[x][y][t] = f;
    return f;
}

//B win:true
bool solve2(int x, int y, int t) {
    if (x == y) return true;
    if (t < 0) assert(false);
    if (used2[x][y][t]) return dp2[x][y][t];
    bool f = !solve1(x, y, t-1);
    for (int d: g[y]) {
        if (!solve1(x, d, t-1)) {
            f = true;
            break;
        }
    }
    used2[x][y][t] = true;
    dp2[x][y][t] = f;
    return f;
}

int main() {
    int m;
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    int x, y;
    cin >> x >> y;
    if (solve1(x, y, n*n+10)) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
    return 0;
}
using namespace std;

typedef long long ll;
typedef pair<int, int> P;
typedef tuple<int, int, int> T;

template<int V>
struct BitMatching {
    vector<int> G[2*V];
    int match[2*V];
    bool used[2*V];
    
    void add_edge(int a, int b) {
        G[a].push_back(b+V);
        G[b+V].push_back(a);
    }

    bool dfs(int v) {
        used[v] = true;
        for (int i = 0; i < G[v].size(); i++) {
            int u = G[v][i], w = match[u];
            if (w < 0 || (!used[w] && dfs(w))) {
                match[v] = u;
                match[u] = v;
                return true;
            }
        }
        return false;
    }

    int bit_matching() {
        int res = 0;
        fill_n(match, 2*V, -1);
        for (int v = 0; v < V; v++) {
            if (match[v] < 0) {
                fill_n(used, 2*V, false);
                if (dfs(v)) {
                    res++;
                }
            }
        }
        return res;
    }
};

//prims.size -> N=100k:near10k
template <int N>
struct Primes {
    bool used[N] = {};
    std::vector<int> primes;
    Primes() {
        for (int i = 2; i < N; i++) {
            if (!used[i]) {
                primes.push_back(i);
            }
            for (int j = i; j < N; j += i) {
                used[j] = true;
            }
        }
    }
};
const int MN = 3100;
BitMatching<MN> b;
Primes<MN> p;
int add[MN];
int c[MN];
bool used[MN];
int main() {
    for (int i = 1; i < MN; i++) {
        int j = i;
        for (int d: p.primes) {
            while (!(j%d)) {
                j /= d;
                c[i]++;
            }
        }
        c[i] %= 2;
    }
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        int d;
        cin >> d;
        assert(d);
        used[d] = true;
    }
    for (int i = 1; i < MN; i++) {
        if (!used[i]) continue;
        for (int d: p.primes) {
            int j = i*d;
            if (j >= MN) break;
            if (!used[j]) continue;
            int ii = i, jj = j;
            if (c[i]) swap(ii, jj);
            b.add_edge(ii, jj);
        }
    }
    cout << n-b.bit_matching() << endl;
    return 0;
}