【luogu CF1140F】Extending Set of Points(线段树分治)

Extending Set of Points

题目链接:luogu CF1140F

题目大意

有一个点集,有一个扩展操作是加入符合条件的 (x0,y0) 直到不能加入位置。
符合条件是原来 (x0,y0) 不存在而且存在 (a,b) 使得 (a,b),(a,y0),(x0,b) 都在点集中。
然后一开始点集为空,会加入或者删除点集,每次问你如果要扩展最后点集有多少个点,注意不会真的扩展。

思路

考虑到怎样会扩展,就是你比如有一列的点,列的位置是一个集合 S,然后某个位置的点所在的行有一个点。
那这个点的那里一列上所有 S 集合上的位置最后都会扩展的有点。

那你发现,如果行和列给它分别建点,然后一个点就把它所在的行和列连边。
首先是二分图,接着你根据上面的性质,其实只要某个行点能走到某个列点,那这个行列的位置就会有点。
那总结一下最后会形成一个类似矩形,每个行的和每个列的线形成的所有交点都有点。
那就是你行点的个数乘列点的个数。

于是处理好了一次的情况,考虑多次询问。
发现是插入和删除,那每个点出现的时刻是一个区间。
然后并查集的话你要删除也不是不行,改成按秩合并。
但是要按顺序删除,按栈的顺序,所以我们上线段树分治即可。

代码

#include<map> #include<cstdio> #include<vector> #define ll long long using namespace std; const int N = 3e5 + 100; int n; map <pair <int, int>, int> a; ll ans[N]; struct Bing_cha_ji { int sta[N], fa[N << 1]; ll ans, nx[N << 1], ny[N << 1]; void Init() { for (int i = 1; i <= n + n; i++) fa[i] = i; for (int i = 1; i <= n; i++) nx[i] = 1, ny[i + n] = 1; } int find(int now) { if (fa[now] == now) return now; return find(fa[now]); } void insert(pair <int, int> W) { int x = W.first, y = W.second + n; x = find(x); y = find(y); if (x == y) return ; ans -= nx[x] * ny[x] + nx[y] * ny[y]; if (nx[x] + ny[x] <= nx[y] + ny[y]) swap(x, y); fa[y] = x; sta[++sta[0]] = y; nx[x] += nx[y]; ny[x] += ny[y]; ans += nx[x] * ny[x]; } void pop() { int y = sta[sta[0]--], x = fa[y]; ans -= nx[x] * ny[x]; nx[x] -= nx[y]; ny[x] -= ny[y]; fa[y] = y; ans += nx[x] * ny[x] + nx[y] * ny[y]; } }B; struct XD_tree { vector <pair<int, int> > f[N << 2]; void insert(int now, int l, int r, int L, int R, pair <int, int> x) { if (L <= l && r <= R) { f[now].push_back(x); return ; } int mid = (l + r) >> 1; if (L <= mid) insert(now << 1, l, mid, L, R, x); if (mid < R) insert(now << 1 | 1, mid + 1, r, L, R, x); } void build(int now, int l, int r) { int lst = B.sta[0]; for (int i = 0; i < f[now].size(); i++) B.insert(f[now][i]); if (l == r) { ans[l] = B.ans; while (B.sta[0] > lst) B.pop(); return ; } int mid = (l + r) >> 1; build(now << 1, l, mid); build(now << 1 | 1, mid + 1, r); while (B.sta[0] > lst) B.pop(); } }T; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { int x, y; scanf("%d %d", &x, &y); if (!a[make_pair(x, y)]) { a[make_pair(x, y)] = i; } else { T.insert(1, 1, n, a[make_pair(x, y)], i - 1, make_pair(x, y)); a.erase(make_pair(x, y)); } } for (map <pair <int, int>, int> ::iterator it = a.begin(); it != a.end(); it++) T.insert(1, 1, n, it->second, n, it->first); B.Init(); T.build(1, 1, n); for (int i = 1; i <= n; i++) printf("%lld ", ans[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_CF1140F.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示