CS Academy #43 D - Bad Triplet

https://csacademy.com/contest/round-43/task/bad-triplet/

問題概要

N頂点M辺の単純無向連結グラフが与えられる。各辺の長さはいずれも1である。このグラフからある3つの頂点A, B, Cを選んだとき、A-B間, A-C間, B-C間の最短距離が等しくなるようなA, B, Cの組合せが存在するか判定せよ。また存在するならば具体的にひとつ示せ。

N, M <= 105

解法

まずグラフに次数3以上の頂点が存在する場合。この頂点をX, この頂点に繋がる点から適当に3つ選んだものをA, B, Cとおくと、A-B間に辺がある場合はA, B, X, A-C間に辺がある場合はA, C, X, B-C間に辺がある場合はB, C, Xを答えればよい(いずれも自明に最短距離1)。どの辺もない場合はA, B, Cが答えになる(Xを中心にして距離2)。

次に次数3以上の頂点がない場合。このときグラフは必ず1本のパスか1つのサイクルになる。パスになる場合答えは存在しない。サイクルになる場合、Nが3の倍数ならば等間隔に3つ点を取ればよく、そうでないならば答えは存在しない。

感想

天才か????????????????

コード (C++)

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for (int i=0;i<(n);i++)
#define REP2(i,m,n) for (int i=m;i<(n);i++)
typedef long long ll;

int N, M;
vector<int> edges[101010];

int main() {
    cin >> N >> M;
    REP(i, M) {
        int a, b; cin >> a >> b;
        edges[a - 1].push_back(b - 1);
        edges[b - 1].push_back(a - 1);
    }


    REP(i, N) if (edges[i].size() >= 3) {
        int a = edges[i][0];
        int b = edges[i][1];
        int c = edges[i][2];
        
        if (find(edges[a].begin(), edges[a].end(), b) != edges[a].end()) {
            cout << i + 1 << " " << a + 1 << " " << b + 1 << endl;
            return 0;
        }

        if (find(edges[a].begin(), edges[a].end(), c) != edges[a].end()) {
            cout << i + 1 << " " << a + 1 << " " << c + 1 << endl;
            return 0;
        }

        if (find(edges[b].begin(), edges[b].end(), c) != edges[b].end()) {
            cout << i + 1 << " " << b + 1 << " " << c + 1 << endl;
            return 0;
        }

        cout << a + 1 << " " << b + 1 << " " << c + 1 << endl;
        return 0;
    }


    int d_min = 1 << 29;
    REP(i, N) d_min = min(d_min, (int)edges[i].size());

    if (d_min == 1 || N % 3) {
        cout << -1 << endl;
        return 0;
    }


    deque<pair<int, int>> q;
    vector<bool> used(N, 0);
    vector<int> dist(N, 1 << 29);
    q.push_back({0, 0});
    
    while (!q.empty()) {
        int n = q.front().first;
        int d = q.front().second;
        q.pop_front();
        if (used[n]) continue;
        
        dist[n] = d;
        used[n] = true;

        for (auto m: edges[n]) if (!used[m]) q.push_back({m, d + 1});
    }


    REP(i, N) if (dist[i] == N / 3) cout << i + 1 << " ";
    cout << 1 << endl;
}