【題解】[JOISC2020] ジョイッターで友だちをつくろう
阿新 • • 發佈:2021-06-18
關於我讀錯題調了一個小時這檔事,這個關注操作不具有傳遞性,也就是說如果 \(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; }