uvalive 4730王国kingdom(并查集+线段树)



题意:有T组测试数据,每组数据的N表示有N个城市,接下来的N行里每行给出每个城市的坐标(0<=x,y<=1000000),然后有M(1<M<200000)个操作,操作有两类,(1)"road A B",表示将城市A和城市B通过一条道路连接,如果A和B原来属于不同的城市群,经过这个操作,A和B就在一个城市群里了,保证每条道路不会和其他道路相交(除了端点A和B)。(2)"line C",表示查询当穿过y=C的直线,有多少个城市群、这几个城市群一共有多少个城市。

思路:线段树加并查集

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#define eps 1e-6
#define LL long long
using namespace std;  

const int maxn = 100000 + 100;
const int maxl = 1000000 + 10;
const int INF = 0x3f3f3f3f;
int pa[maxn], low[maxn], high[maxn], pos[maxn], node[maxn];
int sumv1[2*maxl], addv1[2*maxl];  //有多少州
int sumv2[2*maxl], addv2[2*maxl];
int n, m;
//pa保存父亲结点,low,high保存该连通分量的上下边界,node保存连通分量中的结点个数
int find(int x) {
	if(x != pa[x]) return pa[x] = find(pa[x]);
	return x;
}

void maintain1(int o, int L, int R) {
	int lc = o*2, rc = o*2+1;
	sumv1[o] = 0;
	if(R > L) {   //考虑左右子树
		sumv1[o] = sumv1[lc] + sumv1[rc];
	}
	sumv1[o] += addv1[o] * (R-L+1);//考虑add操作
}
void update1(int o, int L, int R, int v, int yl, int yr) {
	int lc = o*2, rc = o*2+1;
	if(yl <= L && yr >= R) {   //递归边界
		addv1[o] += v;		//累加边界的add值
	} else {
		int M = L + (R-L)/2;
		if(yl <= M) update1(lc, L, M, v, yl, yr);
		if(yr > M) update1(rc, M+1, R, v, yl, yr);
	}
	maintain1(o, L, R);		//递归结束前重新计算本节点的附加信息
}
int query1(int o, int L, int R, int add, int yl, int yr) {
	if(yl <= L && yr >= R) {
		return sumv1[o] + add*(R-L+1);
	} else {
		int ans = 0;
		int M = L + (R-L)/2;
		if(yl <= M) ans += query1(o*2, L, M, add + addv1[o], yl, yr);
		if(yr > M)  ans += query1(o*2+1, M+1, R, add + addv1[o], yl, yr);
		return ans;
	}
} 

void maintain2(int o, int L, int R) {
	int lc = o*2, rc = o*2+1;
	sumv2[o] = 0;
	if(R > L) {   //考虑左右子树
		sumv2[o] = sumv2[lc] + sumv2[rc];
	}
	sumv2[o] += addv2[o] * (R-L+1);//考虑add操作
}
void update2(int o, int L, int R, int v, int yl, int yr) {
	int lc = o*2, rc = o*2+1;
	if(yl <= L && yr >= R) {   //递归边界
		addv2[o] += v;		//累加边界的add值
	} else {
		int M = L + (R-L)/2;
		if(yl <= M) update2(lc, L, M, v, yl, yr);
		if(yr > M) update2(rc, M+1, R, v, yl, yr);
	}
	maintain2(o, L, R);		//递归结束前重新计算本节点的附加信息
}
int query2(int o, int L, int R, int add, int yl, int yr) {
	if(yl <= L && yr >= R) {
		return sumv2[o] + add*(R-L+1);
	} else {
		int ans = 0;
		int M = L + (R-L)/2;
		if(yl <= M) ans += query2(o*2, L, M, add + addv2[o], yl, yr);
		if(yr > M)  ans += query2(o*2+1, M+1, R, add + addv2[o], yl, yr);
		return ans;
	}
} 

void init() {
	memset(addv1, 0 ,sizeof(addv1)); memset(addv2, 0, sizeof(addv2));
	memset(sumv1, 0, sizeof(sumv1)); memset(sumv2, 0, sizeof(sumv2));
	cin >> n;
	for(int i = 0; i < n; i++) {
		int tmp; cin >> tmp >> pos[i];
	}
	for(int i = 0; i < n; i++) {
		pa[i] = i;
		node[i] = 1;
		high[i] = low[i] = pos[i];
	}
	cin >> m;
}

void solve() {
	char cmd[5];
	int a, b;
	float c;
	while(m--) {
		cin >> cmd;
		if(cmd[0] == 'r') {
			cin >> a >> b;
			if(find(a) != find(b)) {
				if(high[pa[a]] != low[pa[a]]) {
					update1(1, 1, 1000000, -1, low[pa[a]]+1, high[pa[a]]);
					update2(1, 1, 1000000, -node[pa[a]], low[pa[a]]+1, high[pa[a]]);
				}
				if(high[pa[b]] != low[pa[b]]) {
					update1(1, 1, 1000000, -1, low[pa[b]]+1, high[pa[b]]);
					update2(1, 1, 1000000, -node[pa[b]], low[pa[b]]+1, high[pa[b]]);
				}
				node[pa[a]] += node[pa[b]];
				low[pa[a]] = min(low[pa[a]], low[pa[b]]);
				high[pa[a]] = max(high[pa[a]], high[pa[b]]);
				pa[pa[b]] = pa[a];
				if(high[pa[a]] != low[pa[a]]) {
					update1(1, 1, 1000000, 1, low[pa[a]]+1, high[pa[a]]);
					update2(1, 1, 1000000, node[pa[a]], low[pa[a]]+1, high[pa[a]]);
				}
			}
		} else {
			cin >> c;
			cout << query1(1, 1, 1000000, 0, (int)(c+1), (int)(c+1)) << " ";
			cout << query2(1, 1, 1000000, 0, (int)(c+1), (int)(c+1)) << endl;
		//	cout << (int)(c+1) << endl;
		}
	}
}

int main() {
//	freopen("input.txt", "r", stdin);
	int t; cin >> t;
	while(t--) {
		init();
		solve();
	}
	return 0;
}
时间: 2024-10-10 14:31:38

uvalive 4730王国kingdom(并查集+线段树)的相关文章

UVA1455 - Kingdom(并查集 + 线段树)

UVA1455 - Kingdom(并查集 + 线段树) 题目链接 题目大意:一个平面内,给你n个整数点,两种类型的操作:road x y 把city x 和city y连接起来,line fnum (浮点数小数点一定是0.5) 查询y = fnum这条直线穿过了多少个州和city.州指的是连通的城市. 解题思路:用并查集记录城市之间是否连通,还有每一个州的y的上下界.建立坐标y的线段树,然后每次运行road操作的时候,对范围内的y坐标进行更新:更新须要分三种情况:两个州是相离,还是相交,还是包

uva 1455 - Kingdom(并查集+线段树)

题目链接:uva 1455 - Kingdom 题目大意:平面上又n个城市,初始时城市之间没有任何双向道路相连,要求一次执行指令. road A B :在城市A和城市B之间连接一条双向道路 line C:询问一条y=C的水平线上穿过多少州和这些州总共有多少城市. 一个联通分量算一个州,C保证为小数部分为0.5的实数. 解题思路:线段树维护每个位置上州和城市的个数,并查集维护哪些城市属于同一个州,并且要记录这些州上下范围.每次新建一条道路,要相应根据两个州的y坐标范围对线段树进行维护. #incl

并查集 + 线段树 LA 4730 Kingdom

题目传送门 题意:训练指南P248 分析:第一个操作可以用并查集实现,保存某集合的最小高度和最大高度以及城市个数.运用线段树成端更新来统计一个区间高度的个数,此时高度需要离散化.这题两种数据结构一起使用,联系紧密. #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; const int M = 3 * N; const int INF = 0x3f3f3f3f; struct Point { int x, y

bzoj2733 [ HNOI2012 ] -- 并查集+线段树合并

用并查集记录每个联通块的根节点,每个联通块建一棵线段树,合并时合并线段树就可以了. 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define N 100010 6 struct node{ 7 int l,r,x; 8 }c[N*20]; 9 int i,j,k,n,m,l,r,a[N],x,y,Rt[N],Num,b[N],f[N],

[BZOJ2733] [HNOI2012]永无乡(并查集 + 线段树合并)

传送门 一看到第k大就肯定要想到什么权值线段树,主席树,平衡树之类的 然后就简单了 用并查集判断连通,每个节点建立一颗权值线段树,连通的时候直接合并即可 查询时再二分递归地查找 时间复杂度好像不是很稳定...但hzwer都用这种方法水过.. 正解好像是平衡树+启发式合并,以后学TT #include <cstdio> #include <iostream> #define N 100001 int n, m, q, cnt; int a[N], f[N], sum[N * 20],

BZOJ 3319 黑白树 并查集+线段树

这这这这这这什么毒瘤题!!!!!!!!!!!!!!!!!!!!!!!!!!!! 卡LCT(优秀的LCT由于是均摊本身就带着2,3的常数在,而且这道题对于LCT标记十分难维护,又得乘上4,5然后就炸了),卡树剖,卡正解,但是暴力能A!!!!!!!!!!!!!!!!!!!!!! 所谓正解就是线段树为护dfs序+并查集删点去重,这东西在每个点一秒的时候都过不了Po姐都虚. 但是我在网上看到一个大佬有一个神思路A掉了 下面是我改过之后的的TLE程序 #include<cstdio> #include&

bzoj 2733 永无乡 - 并查集 - 线段树

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输

YYHS-猜数字(并查集/线段树维护)

题目描述 LYK在玩猜数字游戏. 总共有n个互不相同的正整数,LYK每次猜一段区间的最小值.形如[li,ri]这段区间的数字的最小值一定等于xi. 我们总能构造出一种方案使得LYK满意.直到-- LYK自己猜的就是矛盾的! 例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的. 你需要告诉LYK,它第几次猜数字开始就已经矛盾了. 输入 第一行两个数n和T,表示有n个数字,LYK猜了T次.    接下来T行,每行三个数分别表示li,ri和xi. 输出 输出一个数表示第几次开

Codeforces Gym 101194G Pandaria (2016 ACM-ICPC EC-Final G题, 并查集 + 线段树合并)

题目链接  2016 ACM-ICPC EC-Final Problem G 题意  给定一个无向图.每个点有一种颜色. 现在给定$q$个询问,每次询问$x$和$w$,求所有能通过边权值不超过w的边走到$x$的点的集合中,哪一种颜色的点出现的次数最多. 次数相同时输出编号最小的那个颜色.强制在线. 求哪种颜色可以用线段树合并搞定. 关键是这个强制在线. 当每次询问的时候,我们先要求出最小生成树在哪个时刻恰好把边权值不超过$w$的边都用并查集合并了. 在做最小生成树的时候每合并两个节点,另外开一个