现在有 \(n\) 棵树,第 \(i\) 棵树只有一个节点,其编号为 \(0\),这个节点也是这棵树的特殊点。
接下来进行 \(m\) 次操作:
\(n\le 10^5,m\le 2\times 10^5\)
发现第三种操作和前两种操作的执行顺序无关。
所以考虑对于第 \(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 ;
}
原文:https://www.cnblogs.com/Soulist/p/13676130.html