zoukankan      html  css  js  c++  java
  • ZJOI2016 大森林

    [ZJOI2016]大森林

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

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

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

    (nle 10^5,mle 2 imes 10^5)

    Solution

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

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

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

    由于询问保证了不存在 (u,v ot 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
    ", ans[i] ) ; 
    	return 0 ;
    }
    
  • 相关阅读:
    "gcc: cannot specify -o when generating multiple output files"解决方案
    VSCode 回退到上一层
    Mac下的clion配置c/c++环境
    安装homebrew报错error: Not a valid ref: refs/remotes/origin/master 的解决方法
    正则多个空格替换成一个空格 多个相同的字符值保留一个
    python for 循环的速度
    下载器
    requests content 和text 编码格式
    博客园积分排名
    python QQ 发送邮件功能
  • 原文地址:https://www.cnblogs.com/Soulist/p/13676130.html
Copyright © 2011-2022 走看看