题目大意:
你有个点集合
S
S
S,每次往集合里面加点或者删点(如果要加的点出现过),如果
(
x
1
,
y
1
)
,
(
x
2
,
y
1
)
,
(
x
1
,
y
2
)
,
(
x
2
,
y
2
)
(x1,y1),(x2,y1),(x1,y2),(x2,y2)
(x1,y1),(x2,y1),(x1,y2),(x2,y2)只要出现其中3个第4个就会出现?
问你最终这个点集会变成多少个点在里面
解题思路:
很明显的一点4个点连线会构成一个环,我们对这个点我们可以直接用并查集去维护行和列拆开
例如
(
x
1
,
y
1
)
(x1,y1)
(x1,y1)我们直接把
x
1
←
→
(
y
1
+
n
)
x1\leftarrow\rightarrow (y1+n)
x1←→(y1+n)两个集合合并,当
x
2
←
→
y
1
+
n
,
x
1
←
→
y
2
+
n
x2\leftarrow\rightarrow y1+n,x1\leftarrow\rightarrow y2+n
x2←→y1+n,x1←→y2+n
的时候
x
2
←
→
y
2
+
n
x2\leftarrow\rightarrow y2+n
x2←→y2+n是不是也成立了!!!
现在就是怎么求最终有多少个点在里面?
我们不考虑删除操作只考虑加点该怎么办?
我们在合并两个集合的时候我们发现新增的点数本质上就是两个集合里面的(x,y) 互相配对,参生的结果那么,我们只有维护每个块里面多少个x点,多少个y点就可以了!!
对于删除操作,我们可以直接用线段树分治去搞,用可撤销并查集维护结果就可以了
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 700010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
read(first);
read(args...);
}
map<PII,int> tim;
int q;
struct dsu {
int fa[maxn];
vector<PII> loop;
int siz[maxn];// 维护块的大小
int sizn[maxn];// 维护y的个数
inline void init(int n) {
loop.clear();
for(int i = 1; i <= n; ++ i) fa[i] = i, siz[i] = 1;
for(int i = n+1; i <= 2 * n; ++ i) fa[i] = i, siz[i] = 1, sizn[i] = 1;// 初始化y
}
inline int find(int u) { // 可撤销并查集是按秩合并。
while(u != fa[u]) u = fa[u];
return u;
}
}dsu;
ll ans = 0;
struct Segtree {
vector<PII> tr[maxn<<2];
inline void add(int rt, int l, int r, int posl, int posr, PII val) {
if(l >= posl && r <= posr) {
tr[rt].push_back(val);
return;
}
if(posl <= mid) add(Lson,posl,posr,val);
if(posr > mid) add(Rson,posl,posr,val);
}
inline void dfs(int rt, int l, int r) {
int las = dsu.loop.size();
ll backup = ans;
for(auto it : tr[rt]) {
int fx = dsu.find(it.first);
int fy = dsu.find(it.second+300000);
if(fx == fy) continue;
if(dsu.siz[fx] > dsu.siz[fy]) swap(fx,fy);
ans += 1ll * dsu.sizn[fx] * (dsu.siz[fy] - dsu.sizn[fy]);
ans += 1ll * dsu.sizn[fy] * (dsu.siz[fx] - dsu.sizn[fx]);
dsu.fa[fx] = fy;
dsu.siz[fy] += dsu.siz[fx];
dsu.sizn[fy] += dsu.sizn[fx];
dsu.loop.push_back({fx,fy});
}
if(l == r) {
cout << ans << " ";
} else {
dfs(Lson);
dfs(Rson);
}
ans = backup;
while(las < dsu.loop.size()) {
auto bac = dsu.loop.back();
dsu.loop.pop_back();
dsu.fa[bac.first] = bac.first;
dsu.siz[bac.second] -= dsu.siz[bac.first];
dsu.sizn[bac.second] -= dsu.sizn[bac.first];
}
}
}sgt;
int main() {
IOS;
cin >> q;
for(int i = 1; i <= q; ++ i) {
int u, v;
cin >> u >> v;
if(!tim.count({u,v})) tim[{u,v}] = i;
else {
sgt.add(1,1,q,tim[{u,v}],i-1,(PII){u,v});
tim.erase({u,v});
}
}
for(auto it : tim) {
sgt.add(1,1,q,it.second,q,it.first);
}
dsu.init(300000);
sgt.dfs(1,1,q);
return 0;
}
/*
3
1 1
1 2
2 1
*/