线段树分治 ---- F. Extending Set of Points(线段树分治 + 可撤销并查集)

题目链接


题目大意:

你有个点集合 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 x2y1+n,x1y2+n
的时候 x 2 ← → y 2 + n x2\leftarrow\rightarrow y2+n x2y2+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
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值