• 「JOISC 2020」有趣的 Joitter 交友


    传送门

    考虑转化为图的模型,不难发现题目要求的就是总边数。

    我们定义两种边:

    1. 两个人单向关注:用单向边相连;
    2. 两个人互相关注:用双向边相连。

    不难发现一个联通块内如果全都由双向边相连,那么就会自动连成一个完全图,它的贡献就是 (siz(siz-1))

    于是我们考虑维护具有这样性质的联通块,我们用 (in_i) 维护向该联通块有单向边的点集, (out_i) 维护该联通块由单向边连出的联通块,并记录由那个点连出的。

    那么我们就很好维护了,加入一条边时,讨论一下这两个联通块是否会合并成大联通块(启发式合并),递归处理并不断更新答案。

    参考代码:

    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <set>
    using namespace std;
    
    template < class T > void read(T& s) {
        s = 0; int f = 0; char c = getchar();
        while ('0' > c || c > '9') f |= c == '-', c = getchar();
        while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
        s = f ? -s : s;
    }
    
    typedef long long LL;
    const int _ = 1e5 + 5;
    
    int n, m, fa[_], siz[_]; LL ans;
    set < int > in[_];
    set < pair < int, int > > out[_];
    
    int Find(int x) { return fa[x] == x ? x : fa[x] = Find(fa[x]); }
    
    void Add_edge(int u, int v) {
        int fu = Find(u), fv = Find(v);
        if (fu == fv || in[fv].find(u) != in[fv].end()) return ;
        auto it = out[fv].lower_bound({ fu, 0 });
        if (it == out[fv].end() || it -> first != fu) {
            ans += siz[fv], in[fv].insert(u), out[fu].insert({ fv, u }); return ;
        }
        vector < int > tin;
        vector < pair < int, int > > tout;
        if (in[fu].size() + out[fu].size() < in[fv].size() + out[fv].size()) swap(fu, fv), swap(u, v);
        for (auto i : out[fv]) {
            in[i.first].erase(i.second);
            ans -= siz[i.first];
            if (i.first != fu) tout.push_back(i);
        }
        out[fv].clear();
        ans += 2ll * siz[fu] * siz[fv] + 1ll * in[fu].size() * siz[fv] - 1ll * in[fv].size() * siz[fv];
        for (auto i : in[fv]) {
            int fi = Find(i);
            out[fi].erase({ fv, i });
            if (fi != fu) tin.push_back(i);
        }
        in[fv].clear();
        fa[fv] = fu, siz[fu] += siz[fv];
        for (auto i : tin) Add_edge(i, fu);
        for (auto i : tout) Add_edge(i.second, i.first);
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
    #endif
        read(n), read(m);
        for (int i = 1; i <= n; ++i) fa[i] = i, siz[i] = 1;
        for (int u, v, i = 1; i <= m; ++i)
            read(u), read(v), Add_edge(u, v), printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    MySQL分页实现
    一周自学动态站点设计
    hdu 1233(还是畅通project)(prime算法,克鲁斯卡尔算法)(并查集,最小生成树)
    windows下使用lighttpd+php(fastcgi)+mysql
    Thinkpad E431 解决无线网卡无法开启
    创建与删除索引
    IC芯片
    Linux IPC(Inter-Process Communication,进程间通信)之管道学习
    POJ 3090 Visible Lattice Points 欧拉函数
    多区域显示(3)
  • 原文地址:https://www.cnblogs.com/zsbzsb/p/13051646.html
Copyright © 2020-2023  润新知