A All-Star Game
题意:有
n
n
n个运动员,
m
m
m个粉丝。粉丝
i
i
i会去看运动员
j
j
j的比赛需要满足两个条件之一:
1、
i
i
i是
j
j
j的粉丝
2、如果有粉丝
a
a
a是运动员
b
b
b的粉丝,
a
a
a也是
j
j
j的粉丝,
i
i
i是
b
b
b的粉,那么
i
i
i也会去看
j
j
j得比赛
题解:
0、考虑用并查集维护运动员和粉丝之间的关系
1、如果存在孤立的粉丝,那么无论多少运动员都无法吸引全部粉丝。
2、如果加边时,两个并查集都以运动员为根,那么这两个运动员可以去掉一个。
3、如果加的边是一个鼓励粉丝,那么需要的运动员加一
4、由于加边减边的操作十分复杂,考虑离线存储所有的边出现的时间区间(以询问出现的次序为时间轴),在线段树上对应时间区间的点上加上边。
5、从根往下遍历,把该加的边用并查集加上,由于之后还要撤销这条边,所以把并查集当前的状态加到栈中。之后往回遍历的时候,把状态撤销。这即所谓的可撤销并查集。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 5e5 + 7;
struct NODE
{
int fau, fav;
int rku, rkv;
int sum, ans;
} stk[MAXN * 10];
int top;
int rk[MAXN * 3], f[MAXN * 3];
int sum, ans;
int n, m;
int fin(int x) { return x == f[x] ? x : fin(f[x]); }
void unin(int u, int v)
{
top++;
int fu = fin(u), fv = fin(v);
stk[top].fau = fu;
stk[top].fav = fv;
stk[top].rku = rk[fu];
stk[top].rkv = rk[fv];
stk[top].sum = sum;
stk[top].ans = ans;
if (fu == fv)
return;
if (fu <= n && fv > n)
{
sum++;
if (rk[fu] == 1)
ans++;
}
else
{
if (rk[fu] != 1 && rk[fv] != 1)
ans--;
}
if (rk[fu] < rk[fv])
swap(fu, fv);
// rk[fu] += rk[fv];
if (rk[fu] == rk[fv])
rk[fu]++;
f[fv] = fu;
}
void stkpop()
{
f[stk[top].fau] = stk[top].fau;
f[stk[top].fav] = stk[top].fav;
sum = stk[top].sum;
ans = stk[top].ans;
rk[stk[top].fau] = stk[top].rku;
rk[stk[top].fav] = stk[top].rkv;
top--;
}
struct EDGE
{
int u, v;
bool operator<(const EDGE b) const
{
if (u == b.u)
return v < b.v;
return u < b.u;
}
} edg[MAXN * 10];
map<EDGE, int> mp;
int totedg = 0;
struct Node
{
int l, r;
vector<int> vc;
} tree[MAXN << 2];
void build(int i, int l, int r)
{
tree[i].l = l;
tree[i].r = r;
if (l == r)
return;
int mid = (l + r) >> 1;
build(i * 2, l, mid);
build(i * 2 + 1, mid + 1, r);
}
void add(int i, int l, int r, int id)
{
if (tree[i].l >= l && tree[i].r <= r)
{
tree[i].vc.push_back(id);
return;
}
if (tree[i].r < l || tree[i].l > r)
return;
if (tree[i * 2].r >= l)
add(i * 2, l, r, id);
if (tree[i * 2 + 1].l <= r)
add(i * 2 + 1, l, r, id);
}
void dfs(int i, int l, int r)
{
int len = tree[i].vc.size();
for (int j = 0; j < len; j++)
{
int u = edg[tree[i].vc[j]].u;
int v = edg[tree[i].vc[j]].v;
unin(u, v);
}
if (l == r)
{
// cout << "sum = "<< sum << '\n';
if (sum == m)
printf("%d\n", ans);
else
{
printf("-1\n");
}
}
else
{
int mid = (l + r) >> 1;
dfs(i * 2, l, mid);
dfs(i * 2 + 1, mid + 1, r);
}
while (len--)
{
stkpop();
}
}
int le[MAXN * 10], ri[MAXN * 10];
int main()
{
mp.clear();
int q;
scanf("%d %d %d", &n, &m, &q);
sum = 0;
top = 0;
ans = 0;
totedg = 0;
for (int i = 1; i <= n + m; i++)
{
f[i] = i;
rk[i] = 1;
}
for (int i = 1; i <= n; i++)
{
int k;
scanf("%d", &k);
for (int j = 1; j <= k; j++)
{
int v;
scanf("%d", &v);
v += n;
if (
!mp.count({i, v})
// mp.find({i, v}) == mp.end()
)
{
// mp[{u, v}]++;
totedg++;
mp[{i, v}] = totedg;
edg[totedg] = {i, v};
}
le[totedg] = 1;
}
}
build(1, 1, q);
for (int i = 1; i <= q; i++)
{
int u, v;
scanf("%d %d", &v, &u);
v += n;
if (
!mp.count({u, v}))
{
mp[{u, v}] = ++totedg;
edg[totedg] = {u, v};
}
int id = mp[{u, v}];
if (le[id] == 0)
{
le[id] = i;
}
else
{
ri[id] = i - 1;
if (i != 1)
add(1, le[id], ri[id], id);
le[id] = ri[id] = 0;
}
}
for (int i = 1; i <= totedg; i++)
{
if (le[i])
{
ri[i] = q;
add(1, le[i], ri[i], i);
}
}
dfs(1, 1, q);
// cin >> q;
}