UOJ505 【JOISC2020】有趣的Joitter交友

UOJ505 【JOISC2020】有趣的Joitter交友

启发式合并

注意到如果两个点能够互相到达,那么向其中任意一点有连边的必然会与另一个点有连边,所以入度集合是需要合并的。

但是出度集合并不会受影响,那么我们可以对于每个极大的任意两点可以两两到达的连通块,维护这个连通块的点集\(s\),向该连通块有连边的点集\(ip\),向该连通块有连边的连通块编号集合\(in\),该连通块出边连向的连通块编号集合\(out\),这四类集合能够帮助我们维护信息和及时合并。

对于一条边,我们在该边指向的位置统计贡献。容易发现一个集合\(U\)的贡献为\(|s_U|(|s_U-1|)+|s_U||ip_U|\)

对于合并来说,我们只需要开一个队列来维护即可。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#define pr pair<int,int>
#define mp make_pair
#define ll long long
#define IT set<int> :: iterator
#define N 100005
using namespace std;
int n,m,x,y,f[N];
set<int>s[N],in[N],out[N],ip[N];
queue< pr >q;
ll ans;
int getf(int x)
{
    return (f[x]==x)?x:(f[x]=getf(f[x]));
}
ll calc(int x)
{
    return (ll)s[x].size()*(s[x].size()-1)+(ll)s[x].size()*ip[x].size();
}
void combine(int x,int y)
{
    ans-=calc(x),ans-=calc(y);
    if (s[x].size()<s[y].size())
        swap(x,y);
    f[y]=x;
    for (IT it=s[y].begin();it!=s[y].end();++it)
    {
        int u(*it);
        ip[x].erase(u);
        s[x].insert(u);
    }
    for (IT it=ip[y].begin();it!=ip[y].end();++it)
    {
        int u(*it);
        if (!s[x].count(u))
            ip[x].insert(u);
    }
    for (IT it=in[y].begin();it!=in[y].end();++it)
    {
        int u(*it);
        if (u==x)
            continue;
        in[x].insert(u),out[u].erase(u),out[u].insert(x);
        if (in[u].count(x))
            q.push(mp(x,u));
    }
    for (IT it=out[y].begin();it!=out[y].end();++it)
    {
        int u(*it);
        if (u==x)
            continue;
        out[x].insert(u),in[u].erase(y),in[u].insert(x);
        if (out[u].count(x))
            q.push(mp(x,u));
    }
    ans+=calc(x);
}
void solve()
{
    while (!q.empty())
    {
        int x(getf(q.front().first)),y(getf(q.front().second));
        q.pop();
        if (x==y)
            continue;
        combine(x,y);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
        f[i]=i,s[i].insert(i);
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        int fx(getf(x)),fy(getf(y));
        if (fx==fy)
            ; else
        if (in[fx].count(fy))
            q.push(mp(x,y)),solve();else
            {
                ans-=calc(fx),ans-=calc(fy);
                out[fx].insert(fy),in[fy].insert(fx),ip[fy].insert(x);
                ans+=calc(fx),ans+=calc(fy);   
            }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2021-04-18 15:58  GK0328  阅读(56)  评论(0编辑  收藏  举报