bzoj3123: [Sdoi2013]森林

题面传送门



复出的第一道题.. md就遇到坑了..

简单来说就是可持久化线段树+启发式合并啊..

感觉启发式合并好神奇好想学

每一次建边就暴力合并,每一个节点维护从根到它的权值线段树

按照题面的话最省空间的做法就是垃圾回收,但是实在是太慢了..

而且这题有坑,题面说的是多组数据其实只有一组 而且是$T>1$的一组..

然后看给了512MB就不需要垃圾回收,而且很多预处理都tm不用了呢!wqnmdsy

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 80010;
const int lg = 18;
struct node {
	int y, next;
}a[Maxn*2]; int first[Maxn], len;
void ins ( int x, int y ){
	len ++;
	a[len].y = y;
	a[len].next = first[x]; first[x] = len;
}
int n, m, t;
int sum[Maxn*lg*lg], lc[Maxn*lg*lg], rc[Maxn*lg*lg], tot;
int rt[Maxn];
int na[Maxn], b[Maxn], bl;
int fa[Maxn][lg], dep[Maxn];
int faa[Maxn], gs[Maxn];
int ff ( int x ){
	if ( faa[x] == x ) return x;
	return faa[x] = ff (faa[x]);
}
void merge ( int &now, int fnow, int l, int r, int x ){
	if ( !now ) now = ++tot;
	sum[now] = sum[fnow]+1;
	if ( l == r ) return;
	int mid = ( l + r ) >> 1;
	if ( x <= mid ) merge ( lc[now], lc[fnow], l, mid, x ), rc[now] = rc[fnow];
	else merge ( rc[now], rc[fnow], mid+1, r, x ), lc[now] = lc[fnow];
}
void dfs1 ( int x, int f ){
	rt[x] = 0;
	merge ( rt[x], rt[fa[x][0]], 1, bl, na[x] );
	for ( int i = 1; i <= 17; i ++ ) fa[x][i] = fa[fa[x][i-1]][i-1];
	for ( int k = first[x]; k; k = a[k].next ){
		int y = a[k].y;
		if ( y == f ) continue;
		fa[y][0] = x; dep[y] = dep[x]+1;
		dfs1 ( y, x );
	}
}
void change ( int &now, int l, int r, int x ){
	if ( !now ) now = ++tot;
	sum[now] ++;
	if ( l == r ) return;
	int mid = ( l + r ) >> 1;
	if ( x <= mid ) change ( lc[now], l, mid, x );
	else change ( rc[now], mid+1, r, x );
}
int getlca ( int x, int y ){
	if ( dep[x] < dep[y] ) swap ( x, y );
	for ( int i = 17; i >= 0; i -- ){
		if ( dep[fa[x][i]] >= dep[y] ){
			x = fa[x][i];
		}
	}
	if ( x == y ) return x;
	for ( int i = 17; i >= 0; i -- ){
		if ( fa[x][i] != fa[y][i] ){
			x = fa[x][i]; y = fa[y][i];
		}
	}
	return fa[x][0];
}
int getrank ( int now1, int now2, int now3, int p, int l, int r, int k ){
	if ( l == r ) return b[l];
	int mid = ( l + r ) >> 1;
	int ls = sum[lc[now1]]+sum[lc[now2]]-2*sum[lc[now3]];
	if ( na[p] <= mid && na[p] >= l ) ls --;
	if ( ls >= k ) return getrank ( lc[now1], lc[now2], lc[now3], p, l, mid, k );
	else return getrank ( rc[now1], rc[now2], rc[now3], p, mid+1, r, k-ls );
}
void read ( int &x ){
	char c = getchar ();
	for ( ; c > ‘9‘ || c < ‘0‘; c = getchar () );
	x = 0;
	for ( ; c <= ‘9‘ && c >= ‘0‘; c = getchar () ) x = x*10+c-‘0‘;
}
int main (){
	int i, j, k, T;
	read (T);
	tot = 0;
	while ( T -- ){
		len = 0; memset ( first, 0, sizeof (first) );
		read (n); read (m); read (t);
		for ( i = 1; i <= n; i ++ ) rt[i] = 0, faa[i] = i, gs[i] = 1, dep[i] = 1;
		for ( i = 1; i <= n; i ++ ){
			read (na[i]);
			b[i] = na[i];
		}
		sort ( b+1, b+n+1 );
		bl = unique ( b+1, b+n+1 ) - (b+1);
		for ( i = 1; i <= n; i ++ ){
			na[i] = lower_bound ( b+1, b+bl+1, na[i] ) - b;
			change ( rt[i], 1, bl, na[i] );
		}
		for ( i = 1; i <= m; i ++ ){
			int x, y;
			read (x); read (y);
			int fx = ff (x), fy = ff (y);
			if ( gs[fx] > gs[fy] ) swap ( x, y );
			faa[fx] = fy; gs[fy] += gs[fx];
			fa[x][0] = y; dep[x] = dep[y]+1;
			dfs1 ( x, y );
			ins ( x, y ); ins ( y, x );
		}
		char c;
		int lastans = 0;
		while ( t -- ){
			c = getchar ();
			for ( ; c > ‘Z‘ || c < ‘A‘; c = getchar () );
			if ( c == ‘Q‘ ){
				int x, y;
				read (x); read (y); read (k);
				x ^= lastans; y ^= lastans; k ^= lastans;
				int lca = getlca ( x, y );
				lastans = getrank ( rt[x], rt[y], rt[fa[lca][0]], lca, 1, bl, k );
				printf ( "%d\n", lastans );
			}
			else {
				int x, y;
				read (x); read (y);
				x ^= lastans; y ^= lastans;
				int fx = ff (x), fy = ff (y);
				if ( gs[fx] > gs[fy] ) swap ( x, y );
				faa[fx] = fy; gs[fy] += gs[fx];
				fa[x][0] = y; dep[x] = dep[y]+1;
				dfs1 ( x, y );
				ins ( x, y ); ins ( y, x );
			}
		}
		break;
	}
	return 0;
}

  

时间: 2024-10-19 09:38:53

bzoj3123: [Sdoi2013]森林的相关文章

【主席树 启发式合并】bzoj3123: [Sdoi2013]森林

小细节磕磕碰碰浪费了半个多小时的时间 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值.  接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分. Output 对于每

bzoj3123: [Sdoi2013]森林 主席树+启发式合并

思博题加强版,还是思博题,RT,没了. 内存log^2,写了回收的话可以少个log. lca不能用树剖了好悲伤(IoI),讨厌倍增. 没有1A好不爽啊啊啊,最近写思博题只有一道1A的是要退役的节奏(@[email protected]) #include<cstdio> #include<algorithm> #define N 80005 #define M (l+r>>1) using namespace std; char o[2]; int k,m,q,s,t,

[bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值. 接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为"Q x y k"或者"L x y ",其含义见题目描述部分. Output 对于每一个第一类

Luogu_P3302 [SDOI2013]森林【题解】主席树 lca 启发式合并

# Luogu_P3302 [SDOI2013]森林 主席树,启发式合并,lca luogu题面 求树上路径的第k大,树之间还有合并. 明显是主席树再加合并. 先说链上第k大,其实就是$Tx+Ty-Tlca-Tlcafa$ $T$表示权值线段树. 主席树维护的是从根节点到当前节点的前缀和. ask的代码如下: inline int ask(int x,int y,int lcc,int lcf,int l,int r,int k){ if(l==r) return b[l]; int lz=su

[SDOI2013]森林(树上主席树)

[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,

BZOJ3123:[SDOI2013]森林——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=3123 https://www.luogu.org/problemnew/show/P3302 树上主席树操作方法看:http://www.cnblogs.com/luyouqi233/p/8159528.html (BZOJ2588:Count on a tree) 这题要动态树,显然不可能LCT套主席树啊. 那我们完全可以启发式合并一下主席树. 剩下的操作就很简单了. (然而我debug两个小时

[Sdoi2013]森林

/* 平常这种题很常见的思路就是求出dfs序来,然后每次查询的时候就是在主席树上查询 x+y-lca-fa[lca] 的值就行了. 但是这个题要动态的给森林中加边,还是强制在线的,所以就需要考虑换一种方法来维护这个东西. 首先先dfs出每棵树来,然后对于link操作,可以启发式合并两个主席树.这里我们把主席树维护的dfs序变成维护每个点到根的这条路径.所里link的时候假设我们要把x合到y上,那么我们就边dfs x 这棵树,边用当前点的fa作为历史状态的root来更新当前点的root就行了.求l

BZOJ 3123 SDOI2013 森林 可持久化线段树+倍增LCA+启发式合并

题目大意:给定一棵森林,每个点有权值,提供两种操作: 1.查询两点间路径上第k小的权值 2.将两个点之间连一条边 保证连接后仍是一座森林 可持久化线段树部分同Count On A Tree 只是这道题加了个连接操作 对于连接操作我们要用到启发式合并 就是把小的那棵树暴力重建 很简单的一个操作但是可以证明是均摊O(nlogn)的 大小我用了并查集 其实记录根就可以了 此外本题的多组数据是在逗比 记住testcase恒等于1就是了 NND我倍增LCA又写错了0.0 预处理时居然从大往小写的0.0 样

Luogu3302 [SDOI2013]森林

题目蓝链 Description 给你一个森林,你需要支持两个操作: 查询一条路径上第\(k\)小的权值是多少 连接两个点 强制在线 Solution 我们一开始看到这道题,一定会想什么LCT套主席树 乱编的 其实我们只需要主席树就可以了.我们发现这题只需要连边,不需要断边.所以我们可以启发式合并,暴力维护较小的那部分的主席树和倍增数组 时间复杂度\(\mathcal{O}(n \log^2 n)\) 如果你还不会用主席树维护链上第\(k\)大,请右转 传送门 Solution #include