【bzoj4817】[Sdoi2017]树点涂色 LCT+LCA+线段树
题目描述
给出一棵n个点,以1为根的有根树,每个点初始染有互不相同的颜色。定义一条路径的权值为路径上的颜色种类数。现有m次操作,每次操作为以下三种之一:
1 x: 把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y: 求x到y的路径的权值。
3 x y: 在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
输入
第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000
输出
每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值
样例输入
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
样例输出
3
4
2
2
题解
LCT+线段树
这不是和 bzoj3779重组病毒 一样的吗。。。还没有换根操作。。。
使用线段树维护DFS序中区间最大值,然后按照那道题的思路使用LCT即可解决1、3操作。
对于2操作,由$x$到根、$y$到根与$lca(x,y)$到根得到。具体答案为$f[x]+f[y]-2*f[lca]+1$。相当于$i$到根中有$f[i]-1$个颜色分界边,于是$x$到$y$中的颜色分界边数目即为$(f[x]-1)+(f[y]-1)-2*(f[lca]-1)$。所以颜色段数目为分界边数目+1=$f[x]+f[y]-2*f[lca]+1$。
时间复杂度$O(n\log^2 n)$
#include <cstdio> #include <cstring> #include <algorithm> #define N 100010 #define lson l , mid , x << 1 #define rson mid + 1 , r , x << 1 | 1 using namespace std; int n , head[N] , to[N << 1] , next[N << 1] , cnt , pre[N][20] , deep[N] , log[N] , pos[N] , last[N] , tot , mx[N << 2] , tag[N << 2] , fa[N] , c[2][N]; inline void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } inline void pushup(int x) { mx[x] = max(mx[x << 1] , mx[x << 1 | 1]); } void pushdown(int x) { if(tag[x]) { mx[x << 1] += tag[x] , mx[x << 1 | 1] += tag[x]; tag[x << 1] += tag[x] , tag[x << 1 | 1] += tag[x]; tag[x] = 0; } } void update(int b , int e , int a , int l , int r , int x) { if(b <= l && r <= e) { mx[x] += a , tag[x] += a; return; } pushdown(x); int mid = (l + r) >> 1; if(b <= mid) update(b , e , a , lson); if(e > mid) update(b , e , a , rson); pushup(x); } int query(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return mx[x]; pushdown(x); int mid = (l + r) >> 1 , ans = 0; if(b <= mid) ans = max(ans , query(b , e , lson)); if(e > mid) ans = max(ans , query(b , e , rson)); return ans; } void dfs(int x) { int i; pos[x] = ++tot; for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) pre[x][i] = pre[pre[x][i - 1]][i - 1]; for(i = head[x] ; i ; i = next[i]) if(to[i] != pre[x][0]) pre[to[i]][0] = fa[to[i]] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]); last[x] = tot , update(pos[x] , last[x] , 1 , 1 , n , 1); } inline int lca(int x , int y) { int i; if(deep[x] < deep[y]) swap(x , y); for(i = log[deep[x] - deep[y]] ; ~i ; i -- ) if(deep[x] - deep[y] >= (1 << i)) x = pre[x][i]; if(x == y) return x; for(i = log[deep[x]] ; ~i ; i -- ) if(deep[x] >= (1 << i) && pre[x][i] != pre[y][i]) x = pre[x][i] , y = pre[y][i]; return pre[x][0]; } inline bool isroot(int x) { return x != c[0][fa[x]] && x != c[1][fa[x]]; } inline void rotate(int x) { int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1; if(!isroot(y)) c[c[1][z] == y][z] = x; fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y; } inline void splay(int x) { int y , z; while(!isroot(x)) { y = fa[x] , z = fa[y]; if(!isroot(y)) { if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x); else rotate(y); } rotate(x); } } inline void modify(int x , int a) { if(!x) return; while(c[0][x]) x = c[0][x]; update(pos[x] , last[x] , a , 1 , n , 1); } inline void access(int x) { int t = 0; while(x) splay(x) , modify(c[1][x] , 1) , c[1][x] = t , modify(t , -1) , t = x , x = fa[x]; } int main() { int m , i , opt , x , y , z; scanf("%d%d" , &n , &m); for(i = 2 ; i <= n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x) , log[i] = log[i >> 1] + 1; dfs(1); while(m -- ) { scanf("%d%d" , &opt , &x); if(opt == 1) access(x); else if(opt == 2) { scanf("%d" , &y) , z = lca(x , y); printf("%d\n" , query(pos[x] , pos[x] , 1 , n , 1) + query(pos[y] , pos[y] , 1 , n , 1) - 2 * query(pos[z] , pos[z] , 1 , n , 1) + 1); } else printf("%d\n" , query(pos[x] , last[x] , 1 , n , 1)); } return 0; }