제출 #1369243

#제출 시각아이디문제언어결과실행 시간메모리
1369243iamhereforfun조이터에서 친구를 만드는건 재밌어 (JOI20_joitter2)C++20
0 / 100
12 ms28624 KiB
// Starcraft 2 enjoyer //

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2e5 + 5;

int n, m, par[N], sz[N];
long long ans;
// point[root] = unique node IDs following this component
// in[root] = component roots that follow this component
// to[root] = component roots that this component follows
set<int> point[N], in[N], to[N];
queue<pair<int, int>> q;

int getParent(int i) {
    return par[i] == i ? i : par[i] = getParent(par[i]);
}

int getSum(int i) {
    // Internal edges + (size * number of unique external followers)
    return sz[i] * (sz[i] - 1) + sz[i] * (int)point[i].size();
}

void combine(int u, int v) {
    int pi = getParent(u);
    int pj = getParent(v);
    if (pi == pj) return;

    // Small-to-large based on total set activity
    if (sz[pi] + in[pi].size() + to[pi].size() + point[pi].size() < 
        sz[pj] + in[pj].size() + to[pj].size() + point[pj].size()) {
        swap(pi, pj);
    }

    ans -= getSum(pi);
    ans -= getSum(pj);

    // 1. Move individual follower nodes
    for (int node : point[pj]) {
        if (getParent(node) != pi) point[pi].insert(node);
    }
    point[pj].clear();

    // 2. Clean up mutual links before merging sets
    in[pi].erase(pj);
    to[pi].erase(pj);
    in[pj].erase(pi);
    to[pj].erase(pi);

    // 3. Update incoming neighbors: Neighbors pointing to pj now point to pi
    for (int pk : in[pj]) {
        to[pk].erase(pj);
        to[pk].insert(pi);
        in[pi].insert(pk);
        // Check if a new mutual link is formed
        if (to[pi].count(pk)) q.push({pi, pk});
    }
    in[pj].clear();

    // 4. Update outgoing neighbors: pj pointing to pk now pi points to pk
    for (int pk : to[pj]) {
        in[pk].erase(pj);
        in[pk].insert(pi);
        to[pi].insert(pk);
        // Check if a new mutual link is formed
        if (in[pi].count(pk)) q.push({pi, pk});
    }
    to[pj].clear();

    // 5. Finalize DSU merge
    par[pj] = pi;
    sz[pi] += sz[pj];
    ans += getSum(pi);
}

void add_edge(int u, int v) {
    int pu = getParent(u);
    int pv = getParent(v);

    if (pu == pv || point[pv].count(u)) return;

    // The Joitter Rule: If pv already follows pu, and we add u -> v, they merge.
    if (to[pv].count(pu)) {
        q.push({pu, pv});
        while (!q.empty()) {
            auto [curr_u, curr_v] = q.front();
            q.pop();
            combine(curr_u, curr_v);
        }
    } else {
        // Normal follow: update the point set and component tracking
        ans -= getSum(pv);
        point[pv].insert(u);
        in[pv].insert(pu);
        to[pu].insert(pv);
        ans += getSum(pv);
    }
}

inline void solve() {
    if (!(cin >> n >> m)) return;
    ans = 0;
    for (int x = 1; x <= n; x++) {
        sz[x] = 1;
        par[x] = x;
        point[x].clear();
        in[x].clear();
        to[x].clear();
    }
    for (int x = 0; x < m; x++) {
        int a, b;
        cin >> a >> b;
        add_edge(a, b);
        cout << ans << "\n";
    }
}

signed main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    solve();
    return 0;
}
#결과 실행 시간메모리채점기 출력
결과를 불러오는 중입니다…
#결과 실행 시간메모리채점기 출력
결과를 불러오는 중입니다…
#결과 실행 시간메모리채점기 출력
결과를 불러오는 중입니다…