Codeforces 1140F 线段树 分治 并查集

题意及思路:https://blog.csdn.net/u013534123/article/details/89010251

之前cf有一个和这个相似的题,不过那个题只有合并操作,没有删除操作,直接并查集搞一搞就行了。对于这个题,因为有删除操作,我们对操作序列建一颗线段树,记录每个操作影响的区间操作就可以了。这里的并查集不能路径压缩,要按秩合并,这样复杂度是O(logn)的。

代码:

#include <bits/stdc++.h>
#define ls (o << 1)
#define rs (o << 1 | 1)
#define INF 0x3f3f3f3f
#define db double
#define pii pair<int, int>
#define LL long long
using namespace std;
const int maxn = 300010;
const int Base = 300000;
vector<pii> tr[maxn * 4];
map<pii, int> mp;
map<pii, int>::iterator it;
LL res[maxn], cnt_x[maxn * 2], cnt_y[maxn * 2], sz[maxn * 2];
int f[maxn * 2];
LL ans;
pii a[maxn];
int get(int x) {
	if(x == f[x]) return x;
	return get(f[x]);
}
void add(int o, int l, int r, int ql, int qr, pii val) {
	if(l >= ql &&r <= qr) {
		tr[o].push_back(val);
		return;
	}
	int mid = (l + r) >> 1;
	if(ql <= mid) add(ls, l, mid, ql, qr, val);
	if(qr > mid) add(rs, mid + 1, r, ql, qr, val);
}
void del(int x, int y) {
	int x1 = get(x), y1 = get(y);
	if(x1 != y1) return;
	ans -= cnt_x[x] * cnt_y[x];
	cnt_x[x] -= cnt_x[y], cnt_y[x] -= cnt_y[y];
	sz[x] -= sz[y];
	ans += cnt_x[x] * cnt_y[x];
	ans += cnt_x[y] * cnt_y[y];
	f[y] = y;
}
pii merge(int x, int y) {
	int x1 = get(x), y1 = get(y);
	if(x1 == y1) return make_pair(-1, -1);
	if(sz[x1] < sz[y1]) swap(x1, y1);
	ans -= cnt_x[x1] * cnt_y[x1];
	ans -= cnt_x[y1] * cnt_y[y1];
	sz[x1] += sz[y1];
	cnt_x[x1] += cnt_x[y1], cnt_y[x1] += cnt_y[y1];
	ans += cnt_x[x1] * cnt_y[x1];
	f[y1] = x1;
	return make_pair(x1, y1);
}
void dfs(int o, int l, int r) {
	if(l == 12) {
		l++;
		l--;
	}
	stack<pii> s;
	for (auto x : tr[o]) {
		pii tmp = merge(x.first, x.second);
		if(tmp.first != -1) s.push(tmp);
	}
	if(l == r) res[l] = ans;
	else {
		int mid = (l + r) >> 1;
		dfs(ls, l, mid);
		dfs(rs, mid + 1, r);
	}
	while(!s.empty()) {
		del(s.top().first, s.top().second);
		s.pop();
	}
}
int main() {
	int n, x, y;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d%d", &x, &y);
		y += Base;
		a[i] = make_pair(x, y);
		if(mp.find(a[i]) == mp.end()) mp[a[i]] = i;
		else {
			add(1, 1, n, mp[a[i]], i - 1, a[i]);
			mp.erase(a[i]);
		}
	}
	for (it = mp.begin(); it != mp.end(); it++) {
		add(1, 1, n, it -> second, n, it -> first);
	}
	for (int i = 1; i <= Base; i++) {
		f[i] = i, cnt_x[i] = 1, cnt_y[i] = 0, sz[i] = 1;
	}
	for (int i = Base + 1; i <= Base * 2; i++) {
		f[i] = i, cnt_x[i] = 0, cnt_y[i] = 1, sz[i] = 1;
	}
	dfs(1, 1, n);
	for (int i = 1; i <= n; i++)
		printf("%lld ", res[i]);
}

  

原文地址:https://www.cnblogs.com/pkgunboat/p/11027129.html

时间: 2024-10-12 04:30:22

Codeforces 1140F 线段树 分治 并查集的相关文章

[BZOJ4025]二分图(线段树分治,并查集)

4025: 二分图 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2191  Solved: 800[Submit][Status][Discuss] Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简单的问题神犇当然会做了,于是他想考考你. Input 输入数据的第一行是三个整数n,m,T. 第2行到第m+1行,每行4个整数u,v,start,end

线段树分治总结(线段树分治,线段树,并查集,树的dfn序,二分图染色)

闲话 stO猫锟学长,满脑子神仙DS 线段树分治思想 我们在做CDQ的时候,将询问和操作通通视为元素,在归并过程中统计左边的操作对右边的询问的贡献. 而在线段树分治中,询问被固定了.按时间轴确定好询问的序列以后,我们还需要所有的操作都会影响一个时间区间.而这个区间,毫无疑问正好对应着询问的一段区间. 于是,我们可以将每一个操作丢到若干询问里做区间修改了,而线段树可以高效地维护.我们开一个叶子节点下标为询问排列的线段树,作为分治过程的底层结构. 具体的实现,仍然要看题目. 例题1 BZOJ4025

Codeforces 938G 线段树分治 线性基 可撤销并查集

Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问x到y的路径异或最小值 保证图在任意时刻连通 首先连通图路径异或相当于从x到y的任意一条路径再异或上若干个环得到的,只要在dfs过程中把非树边成的环丢到线性基里就好了,其他环一定可以通过这些环异或组合出来 有加边删边操作怎么做呢?线段树时间分治!注意到不能保证在线段树的任意一个节点图是连通的,需要用

Codeforces.1051G.Distinctification(线段树合并 并查集)

题目链接 \(Description\) 给定\(n\)个数对\(A_i,B_i\).你可以进行任意次以下两种操作: 选择一个位置\(i\),令\(A_i=A_i+1\),花费\(B_i\).必须存在一个位置\(j\),满足\(A_i=A_j,\ i\neq j\),才可以进行. 选择一个位置\(i\),令\(A_i=A_i-1\),花费\(-B_i\).必须存在一个位置\(j\),满足\(A_i=A_j+1\),才可以进行. 你需要对于所有\(i\in[1,n]\),求使得\(A_1,A_2,

算法学习——动态图连通性(线段树分治+按秩合并并查集)

在考场上遇到了这个的板子题,,,所以来学习了一下线段树分治 + 带撤销的并查集. 题目大意是这样的:有m个时刻,每个时刻有一个加边or撤销一条边的操作,保证操作合法,没有重边自环,每次操作后输出当前图下所有联通块大小的乘积. 首先观察到如果没有撤销操作,那么直接用并查集就可以维护,每次合并的时候乘上要合并的两个并查集大小的逆元,然后乘上合并之后的大小即可. 那么来考虑撤销,观察到如果并查集不带路径压缩,应该是可以处理撤销操作的. 但我们并不能直接做,因为并查集的撤销必须按顺序来,就相当于每次合并

线段树分治总结

目录 类型一 例题1:八纵八横 代码: 例题2:时空旅行 首先,要求可以离线. 线段树分治有两种. 类型一 操作基于区间,单点询问. 有时,进行的一种操作可以快速完成,但是,要实现这种操作的逆操作较难. 因为,通常情况下,需要实现的逆操作都是很久以前执行的. 但是,如果只撤销上次操作,就会简单得多. 比如,维护一些连通性,或直径,线性基等问题. 这类问题加边很好做,但删边很难实现. 我们可以扫一遍操作,得到每个操作的有效区间. 然后,将每个添加操作的有效区间按在线段树上,然后遍历这颗线段树同时处

Codeforces 445B DZY Loves Chemistry(并查集)

题目链接:Codeforces 445B DZY Loves Chemistry 题目大意:有若干种化学药品,给出两两会反应的关系,现在要将药物依次放入一个容器中,容器中的化学药品可以互相反应,如果当前放入的药品能与已经在容器中的某一药品反应,那么危险值翻倍,即*2,初始值为1,求一顺序,使得为危险值最大. 解题思路:并查集求最小联通分量s,2n?s即为答案. #include <cstdio> #include <cstring> #include <iostream>

Codeforces 384E 线段树+dfs序

题目链接:点击打开链接 题意: 给定n个点,m个询问的无向树(1为根) 下面n个数表示每个点的权值 下面n-1行给出树 操作1:x点权值+v, x的第 i & 1 的儿子-v, 第 !(i&1) 的儿子+v 操作2:询问x点权值 dfs把树转成序列 根据深度把点分成2组 分别用线段树维护.. 然后Y一下 #include<stdio.h> #include<string.h> #include<iostream> #include<algorith

BZOJ 4025 二分图 分治+并查集

题目大意:给定一张n个点的图,有m条边,T个时间段,每条边只存在于(st,ed]这些时间段,求每个时间段内这个图是否是二分图 分治并查集大法好 定义Solve(x,y,E)为当前处理的区间为[x,y],E为所有存在时间为[x,y]的子集的边的集合 那么对于E中的每一条边(u,v),讨论: 若当前边的存在时间为[x,y],则在并查集上判断是否出现奇环 如果出现,[x,y]内的所有时刻都一定不是二分图,输出答案即可 如果不出现,在并查集中连接(u,v) 否则判断存在时间和mid的关系讨论扔进左区间还