1. 程式人生 > 其它 >【題解】[JOISC2020] ジョイッターで友だちをつくろう

【題解】[JOISC2020] ジョイッターで友だちをつくろう

關於我讀錯題調了一個小時這檔事,這個關注操作不具有傳遞性,也就是說如果 \(x\) 關注 \(y\)\(x\) 所在強連通分量的人不用關注 \(y\) 。。。

題意比較簡單,轉化一下,給定 \(N\) 個點,支援加邊,維護強連通分量,每個分量的貢獻為 \(size^2-size\) ,對於縮點後每條邊的貢獻為終點所在強連通分量的 \(size\) 。再次強調只計算終點的 \(size\) ,筆者因為看成兩端 \(size\) 乘積調了一年。

轉化完後你會發現,這這題怎麼這麼熟悉,**[WC2021] 括號路徑 **是這道題的弱化版,只用維護 \(size^2\) 即可。

所以我們開 set 維護分別維護每一個強連通分量中的點,連向當前分量的其他分量,當前分量出發到達的其他分量,和到達當前分量且不在當前分量中的點。

合併的時候可能會引起多個後續合併,所以我們再用佇列維護一下需要合併的點對即可。

時間複雜度 \(\mathcal{O}(M\log M\log N)\) 。這道題做的人還是少,做過這題參加 WC2021 可收穫巨大 BUFF。

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
using namespace std;
int n, m, col[N];
long long ans;
set<int>s[N], in[N], out[N], ed[N];
queue<pair<int, int>>q;
typedef set<int>::iterator ite;
long long g(int x) {
    return 1LL * s[x].size() * (s[x].size() - 1 + ed[x].size());
}
void merge(int u, int v) {
    q.push(make_pair(u, v));

    while (!q.empty()) {
        int x = col[q.front().first], y = col[q.front().second];
        q.pop();

        //cout<<"ss "<<x<<" "<<y<<endl;
        if (x == y)
            continue;

        if (s[x].size() > s[y].size())
            swap(x, y);

        ans -= g(x) + g(y);

        if (in[x].find(y) != in[x].end())
            in[x].erase(y), out[y].erase(x);

        if (in[y].find(x) != in[y].end())
            in[y].erase(x), out[x].erase(y);

        for (ite it = s[x].begin(); it != s[x].end(); it++) {
            col[*it] = y, s[y].insert(*it);
            ite cur = ed[y].find(*it);

            if (cur != ed[y].end())
                ed[y].erase(cur);
        }

        for (ite it = in[x].begin(); it != in[x].end(); it++) {
            out[*it].erase(x);
            out[*it].insert(y);

            if (out[y].find(*it) != out[y].end())
                q.push(make_pair(*it, y));
            else
                in[y].insert(*it);
        }

        for (ite it = out[x].begin(); it != out[x].end(); it++) {
            in[*it].erase(x);
            in[*it].insert(y);

            if (in[y].find(*it) != in[y].end())
                q.push(make_pair(*it, y));
            else
                out[y].insert(*it);
        }

        for (ite it = ed[x].begin(); it != ed[x].end(); it++)
            if (col[*it] != y)
                ed[y].insert(*it);

        ans += g(y);
    }
}
void ins(int x, int y) {
	int w = x;
    x = col[x];
    y = col[y];

    if (x == y)
        return;

    if (in[x].find(y) != in[x].end())
        merge(x, y);
    else in[y].insert(x), out[x].insert(y), ans -= g(y) , ed[y].insert(w), ans += g(y);
}
int main() {
    scanf("%d%d", &n, &m);
    rep(i, 1, n)col[i] = i, s[i].insert(i);
    rep(i, 1, m) {
        int x, y;
        scanf("%d%d", &x, &y);
        ins(x, y);
        printf("%lld\n", ans);
    }
    return 0;
}