2020牛客多校第八场A-All Star Game

https://ac.nowcoder.com/acm/contest/5673/A

题意

有n个篮球运动员,m个球迷。

一个球迷可能是多个球员的粉丝

选择最少的球员进全明星赛,使得所有球迷都愿意观看(至少一个球迷想看的球员入选)。

想看的球员标准如下

有q个粉丝关系的修改,修改完回答询问。\(1 \le n, m,q \le 2*10^5\)

题解

用map处理出每个粉丝关系存在的时间段,把这个粉丝关系作为一条边插入到这个时间段中,用线段树维护这个时间段,在进入这个时间段时,把只属于这个时间段的边加入关系。

由于只要一个粉丝和另一个粉丝喜欢的球员有相同的,那么两个粉丝喜欢的球员会进行合并,所以我们要求的就是n个球员的联通分量个数,其中不包含鼓励球员的点

我们使用可撤销并查集维护这个数量,具体做法是

首先肯定不能路径压缩,我们选择直接按并查集大小合并,小的并查集向大的合并。

我们一开始把答案设为m,即粉丝的数量

设x为球员,y为球迷

x,y加边的时候,如果本来不连通,且y原来有边,那么ans--

x,y删边的时候,如果删完他们不连通,且y删完还有变,那么ans++

把球迷的sz初始设为1,球员的sz初始设为0,那么直接用联通块的sz判断是否还有球员向球迷的边即可。

但要注意,每个球迷都要有喜欢的球员,要维护并判断一下

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct READ {
    inline char read() {
    #ifdef _WIN32
        return getchar();
    #endif
        static const int IN_LEN = 1 << 18 | 1;
        static char buf[IN_LEN], *s, *t;
        return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
    }
    template <typename _Tp> inline READ & operator >> (_Tp&x) {
        static char c11, boo;
        for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
            if(c11 == -1) return *this;
            boo |= c11 == '-';
        }
        for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
        boo && (x = -x);
        return *this;
    }
} in;

const int N = 5e5 + 50;
#define pii pair<int, int>
map<pii, int> mp;
pii edge[N<<1];
int vis[N<<1];
int link[N];
int ans, num;



int f[N], sz[N];
stack<pii> s;
int find(int x) { return x == f[x] ? x : find(f[x]); }
int merge(int x, int y) {
    x = find(x); y = find(y);
    if (x == y) return 0;
    if (sz[x] < sz[y]) swap(x, y);
    sz[x] += sz[y];
    if (sz[x] >= 2 && sz[y]) ans--;
    f[y] = x;
    s.push(pii(x, y)); return 1;
}
void del(int len) {
    while (!s.empty() && (len--)) {
        int x = s.top().first, y = s.top().second;
        s.pop();
        if (sz[x] < sz[y]) swap(x, y);
        if (sz[x] >= 2 && sz[y]) ans++;
        sz[x] -= sz[y];
        f[y] = y;
    }
}

#define ls (o<<1)
#define rs (o<<1|1)
#define mid ((l+r)>>1)
vector<int> t[N<<2];
void update(int o, int l, int r, int ql, int qr, int id) {
    if (ql > qr) return;
    if (ql <= l && r <= qr) { t[o].push_back(id); return; }
    if (ql <= mid) update(ls, l, mid, ql, qr, id);
    if (qr > mid) update(rs, mid + 1, r, ql, qr, id);
}
void query(int o, int l, int r) {
    int len = 0;
    for (int id : t[o]) {
        link[edge[id].second]++;
        if (link[edge[id].second] == 1) num--;
        len += merge(edge[id].first, edge[id].second);
    }
    if (l == r) {
        if (num) puts("-1");
        else printf("%d\n", ans);
        for (int id : t[o]) {
            link[edge[id].second]--;
            if (link[edge[id].second] == 0) num++;
        }
        del(len);
        return;
    }
    query(ls, l, mid); query(rs, mid + 1, r);
    for (int id : t[o]) {
        link[edge[id].second]--;
        if (link[edge[id].second] == 0) num++;
    }
    del(len);
}
int main() {
    int n, m, q; in >> n >> m >> q;
    int cnt = 0;
    num = ans = m;
    for (int i = 1; i <= n + m; i++) f[i] = i;
    for (int i = n + 1; i <= n + m; i++) sz[i] = 1;
    for (int i = 1; i <= n; i++) {
        int k; in >> k;
        for (int j = 1; j <= k; j++) {
            int x; in >> x; x += n;
            mp[pii(i, x)] = ++cnt;
            edge[cnt] = pii(i, x);
            vis[cnt] = 1;
        }
    }
    for (int i = 1; i <= q; i++) {
        int u, v; in >> v >> u; v += n;
        if (!mp.count(pii(u, v))) {
            mp[pii(u, v)] = ++cnt;
            edge[cnt] = pii(u, v);
        }
        int id = mp[pii(u, v)];
        if (!vis[id]) vis[id] = i;
        else {
            update(1, 1, q, vis[id], i - 1, id);
            vis[id] = 0;
        }
    }
    for (int i = 1; i <= cnt; i++) {
        if (vis[i]) update(1, 1, q, vis[i], q, i);
    }
    query(1, 1, q);
    return 0;
}
posted @ 2020-09-08 14:52  Artoriax  阅读(217)  评论(0编辑  收藏  举报