Loading

【题解】[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;
}
posted @ 2021-06-17 23:13  7KByte  阅读(48)  评论(0编辑  收藏  举报