首页 > 其他 > 详细

ZJOI2016 大森林

时间:2020-09-15 22:56:38      阅读:96      评论:0      收藏:0      [点我收藏+]

[ZJOI2016]大森林

现在有 \(n\) 棵树,第 \(i\) 棵树只有一个节点,其编号为 \(0\),这个节点也是这棵树的特殊点。

接下来进行 \(m\) 次操作:

  • 选择区间 \([l,r]\) 并给区间内的每棵树加入一个新节点,编号为 \(cnt\),连接其和特殊节点,然后 \(cnt\rightarrow cnt+1\),初始 \(cnt=1\)
  • 选择区间 \([l,r]\) 并将特殊节点改为标号为 \(x\) 的点,如果 \(x\) 在第 \(i\) 棵树中不存在,那么对于第 \(i\) 棵树,此操作无效。
  • 查询第 \(i\) 棵树上 \(u,v\) 间的距离,保证 \(u,v\) 在第 \(i\) 棵树上。

\(n\le 10^5,m\le 2\times 10^5\)

Solution

发现第三种操作和前两种操作的执行顺序无关。

所以考虑对于第 \(i\) 棵树维护出其具体形态,然后离线查询,就做完了。

考虑如何维护树的形态,不妨考虑第 \(i\) 棵树和第 \(i-1\) 棵树的形态差,发现可以对操作进行差分,第 \(i\) 棵树和第 \(i-1\) 棵树的形态差仅体现在具体的连边和部分节点的存在上。

由于询问保证了不存在 \(u,v\not \in T_i\) 的性质,所以对于操作 \(1\),我们会发现可以将操作 \(1\) 直接改为全局增加节点。

然后考虑支持插入/删除操作 \(2\) 怎么搞。

对于所有操作 \(1/2\) 构成的序列,将 \(1\) 向最近的 \(2\) 连边,同时将每个 \(2\) 向上一个 \(2\) 连边。

如果某个 \(2\) 存在,那么断开他和上一个 \(2\) 之间的边,然后连向对应节点即可。删除类似。

然后操作 \(1\) 在一段区间内等价于这段区间内这个点点权为 \(1\),或者修改一下 \(2\) 类边的时间啥的也可以。

复杂度 \(\mathcal O((n+m)\log n)\)

如果根据边权有无维护点权来搞大概是可以做的,不过实际上可以当作静态树来做,这样就不需要 makeroot 了。

不过由于我打算复习 LCT,就顺手写上去了。

然后由于虚节点的存在,路径信息不太对,要维护一下深度和,当然可以直接查这个点到根有多少个点在即可。

求 LCA 大概 LCT 也是可以做的,然后就做完了。

调得人想死。换了很多种写法。

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]
#define pb push_back
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < ‘0‘ || cc > ‘9‘ ) {  if( cc == ‘-‘ ) flus = - flus ; cc = getchar() ; }
	while( cc >= ‘0‘ && cc <= ‘9‘ )  cn = cn * 10 + cc - ‘0‘, cc = getchar() ;
	return cn * flus ;
}
const int N = 1e6 + 5 ; 
struct LCT {
	int ch[2], fa, s, w, mk ; 
} t[N] ;
struct node { int x, y, id ; } ;
int n, m, cnt, idx, ans[N], Id[N], ll[N], rr[N] ; 
vector<node> Gx[N] ; 
vector<int> L[N], R[N] ; 
void pushmark(int x) { if( t[x].mk ) swap(ls(x), rs(x)), t[x].mk = 0, t[ls(x)].mk ^= 1, t[rs(x)].mk ^= 1 ; }
void pushup(int x) { t[x].s = t[ls(x)].s + t[rs(x)].s + t[x].w ; }
bool isroot(int x) { return (ls(t[x].fa) != x) && (rs(t[x].fa) != x) ;}
void rotate(int x) {
	int f = t[x].fa, ff = t[f].fa, d = (rs(f) == x), c = t[x].ch[d ^ 1] ; 
	if( !isroot(f) ) t[ff].ch[rs(ff) == f] = x ; t[x].fa = ff, 
	t[f].fa = x, t[x].ch[d ^ 1] = f, t[f].ch[d] = c, t[c].fa = f,
	pushup(f), pushup(x) ; 
}
int st[N], top ; 
void Splay(int x) {
	int u = x ; st[++ top] = u ; 
	while(!isroot(u)) st[++ top] = (u = t[u].fa) ; 
	while( top ) pushmark(st[top]), -- top ; 
	while(!isroot(x)) {
		int f = t[x].fa, ff = t[f].fa ; 
		if(!isroot(f)) (rs(f) == x) ^ (rs(ff) == f) ? rotate(x) : rotate(f) ; 
		rotate(x) ;  
	}
}
void access(int x) {
	for(int y = 0; x; y = x, x = t[x].fa)
	Splay(x), rs(x) = y, pushup(x) ; 
}
void makeroot(int x) { 
	access(x), Splay(x), t[x].mk ^= 1 ; 
}
int LCA(int x, int y) {
	makeroot(1), access(x) ; int lca = y ; 
	for(; y; lca = y, y = t[y].fa ) {
		Splay(y), rs(y) = 0, pushup(y) ; 
	} return lca ; 
}
int qry(int x, int y) { 
	int lca = LCA(x, y), Ans = 0 ; 
	access(x), Splay(x), Ans += t[x].s ;
	access(y), Splay(y), Ans += t[y].s ;
	access(lca), Splay(lca), Ans -= 2 * t[lca].s ; 
	return Ans ; 
}
void link(int x, int y) { 
	makeroot(x), t[x].fa = y ; 
}
int findroot(int x) {
	access(x), Splay(x), pushmark(x) ; 
	while( ls(x) ) pushmark(x = ls(x)) ;
	return x ; 
}
void cut(int x, int y) {
	makeroot(x), findroot(y), t[x].fa = ls(y) = 0, pushup(y) ; 
}
signed main()
{
	n = gi(), m = gi(), idx = m + 1 ; int num = 0, opt, x, y, z ; 
	cnt = 1, Id[idx] = 1, link(1, idx), t[1].s = t[1].w = 1 ; ll[1] = 1, rr[1] = n ; 
	rep( i, 1, m ) t[i].w = t[i].s = 1 ; 
	rep( i, 1, m ) {
		opt = gi(), x = gi(), y = gi() ; 
		if( opt == 0 ) ++ cnt, link( cnt, idx ), ll[cnt] = x, rr[cnt] = y ; 
		if( opt == 1 ) {
			++ idx, z = Id[idx] = gi(), link(idx - 1, idx), x = max( x, ll[z] ), y = min( y, rr[z] ) ;
			if( x <= y ) L[x].pb(idx), R[y].pb(idx) ; 
		} 
		if( opt == 2 ) z = gi(), Gx[x].pb((node){y, z, ++ num}) ;
	}
	rep( u, 1, n ) {
		for(int v : L[u]) cut(v, v - 1), link(v, Id[v]) ;
		for(auto v : Gx[u]) ans[v.id] = qry(v.x, v.y) ;
		for(int v : R[u]) cut(v, Id[v]), link(v, v - 1) ; 
	}
	rep( i, 1, num ) printf("%d\n", ans[i] ) ; 
	return 0 ;
}

ZJOI2016 大森林

原文:https://www.cnblogs.com/Soulist/p/13676130.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!