HDU 6394 Tree 分块 || lct
题意: 给你一颗树, 每一个节点都有一个权值, 如果一个石头落在某个节点上, 他就会往上跳这个的点的权值步。 现在有2种操作, 1 把一个石头放在 x 的位置 询问有跳几次才跳出这棵树, 2 修改某个节点的权值。
解法:树上分块, 用dfs分好块之后。 对于每一块都处理出如果石头落在某个位置之后他跳出这个块之后的位置和次数。
每次更新都自己这一块的所有子节点, 然后找第k个父亲的时候用倍增优化。
对于每次询问都跳到0号点之后,返回所经过的次数。
我们可以对属于同一块内的节点重新建立边, 因为我们在更新的时候不会直接访问到别的块的点, 所以重新建立边,避免遍历不需要的边(快了200ms)。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 4 #define LL long long 5 #define ULL unsigned LL 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define lson l,m,rt<<1 10 #define rson m+1,r,rt<<1|1 11 #define max3(a,b,c) max(a,max(b,c)) 12 #define min3(a,b,c) min(a,min(b,c)) 13 typedef pair<int,int> pll; 14 const int inf = 0x3f3f3f3f; 15 const LL INF = 0x3f3f3f3f3f3f3f3f; 16 const LL mod = (int)1e9+7; 17 const int N = 1e5 + 100; 18 int n, m, b; 19 int head[N], to[N], nt[N]; /// E1 20 int head2[N], to2[N], nt2[N]; /// E2 21 int Stack[N], belong[N]; 22 int jump[N], tto[N], cnt[N]; 23 int tot, top, type, tot2; 24 int anc[N][20]; 25 inline void add2(int u, int v){ 26 to2[tot2] = v; 27 nt2[tot2] = head2[u]; 28 head2[u] = tot2++; 29 } 30 inline void add(int u, int v){ 31 to[tot] = v; 32 nt[tot] = head[u]; 33 head[u] = tot++; 34 } 35 void dfs(int u){ 36 int now = top, v; 37 for(int i = head[u]; ~i; i = nt[i]){ 38 v = to[i]; 39 anc[v][0] = u; 40 for(int i = 1; i < 20; i++) 41 anc[v][i] = anc[anc[v][i-1]][i-1]; 42 dfs(v); 43 if(top-now >= b){ 44 ++type; 45 while(top!=now){ 46 belong[Stack[top--]] = type; 47 } 48 } 49 } 50 Stack[++top] = u; 51 } 52 inline int Find(int x, int k){ 53 for(int i = 19; i >= 0; i--) 54 if((k>>i)&1) x = anc[x][i]; 55 return x; 56 } 57 inline void Update(int x){ 58 int z = jump[x]; 59 if(belong[z] == belong[x]){ 60 tto[x] = tto[z]; 61 cnt[x] = cnt[z] + 1; 62 } 63 else { 64 tto[x] = z; 65 cnt[x] = 1; 66 } 67 } 68 void Build(int x){ 69 Update(x); 70 for(int i = head[x]; ~i; i = nt[i]) 71 Build(to[i]); 72 } 73 inline int solve(int p){ 74 int ret = 0; 75 while(p){ 76 ret += cnt[p]; 77 p = tto[p]; 78 } 79 return ret; 80 } 81 void dUpdate(int x){ 82 Update(x); 83 for(int i = head2[x]; ~i; i = nt2[i]){ 84 dUpdate(to2[i]); 85 } 86 } 87 void dfs2(int x){ 88 for(int i = head[x]; ~i; i = nt[i]){ 89 if(belong[x] == belong[to[i]]){ 90 add2(x, to[i]); 91 } 92 dfs2(to[i]); 93 } 94 } 95 int main(){ 96 int t, x; 97 scanf("%d", &t); 98 while(t--){ 99 scanf("%d", &n); 100 b = sqrt(n); 101 tot = 0;top = 0; type = 0;tot2 = 0; 102 memset(head, -1, sizeof(int)*(n+1)); 103 memset(head2, -1, sizeof(int)*(n+1)); 104 for(int i = 2; i <= n; i++){ 105 scanf("%d", &x); 106 add(x, i); 107 } 108 dfs(1); 109 while(top) belong[Stack[top--]] = type; 110 dfs2(1); 111 for(int i = 1; i <= n; i++){ 112 scanf("%d", &x); 113 jump[i] = Find(i, x); 114 } 115 Build(1); 116 scanf("%d", &m); 117 int op, k; 118 while(m--){ 119 scanf("%d", &op); 120 if(op == 1) { 121 scanf("%d", &x); 122 printf("%d\n", solve(x)); 123 } 124 else { 125 scanf("%d%d", &x, &k); 126 jump[x] = Find(x, k); 127 dUpdate(x); 128 } 129 } 130 } 131 return 0; 132 }
还有1种lct的写法,和弹飞绵羊的写法差不多,唯一有区别的就是找落地点在哪里,直接套lct的板子就好了,再用倍增找下一次去的位置就好了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 4 #define LL long long 5 #define ULL unsigned LL 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define lson l,m,rt<<1 10 #define rson m+1,r,rt<<1|1 11 #define lch tr[x].son[0] 12 #define rch tr[x].son[1] 13 #define max3(a,b,c) max(a,max(b,c)) 14 #define min3(a,b,c) min(a,min(b,c)) 15 typedef pair<int,int> pll; 16 const int inf = 0x3f3f3f3f; 17 const LL INF = 0x3f3f3f3f3f3f3f3f; 18 const LL mod = (int)1e9+7; 19 const int N = 1e5 + 100; 20 int n, m, b; 21 int head[N], to[N], nt[N]; 22 int tot, top, type, tot2; 23 int anc[N][20]; 24 inline void add(int u, int v){ 25 to[tot] = v; 26 nt[tot] = head[u]; 27 head[u] = tot++; 28 } 29 void dfs(int u){ 30 int now = top, v; 31 for(int i = head[u]; ~i; i = nt[i]){ 32 v = to[i]; 33 anc[v][0] = u; 34 for(int i = 1; i < 20; i++) 35 anc[v][i] = anc[anc[v][i-1]][i-1]; 36 dfs(v); 37 } 38 } 39 inline int Find(int x, int k){ 40 for(int i = 19; i >= 0; i--) 41 if((k>>i)&1) x = anc[x][i]; 42 return x; 43 } 44 struct Node{ 45 int son[2], pre; 46 int sz, is_root; 47 inline void init() { 48 son[1] = son[0] = pre = 0; 49 sz = is_root =1; 50 } 51 }tr[N]; 52 void Push_Up(int x){ 53 if(!x) return ; 54 tr[x].sz = tr[lch].sz + tr[rch].sz + 1; 55 } 56 void rotate(int x){ 57 if(tr[x].is_root) return ; 58 int y = tr[x].pre, z = tr[y].pre; 59 int k = x == tr[y].son[1]; 60 tr[y].son[k] = tr[x].son[k^1]; 61 tr[tr[y].son[k]].pre = y; 62 tr[x].son[k^1] = y; 63 tr[y].pre = x; 64 tr[x].pre = z; 65 if(tr[y].is_root) tr[x].is_root = 1, tr[y].is_root = 0; 66 else tr[z].son[ tr[z].son[1] == y] = x; 67 Push_Up(y); 68 69 } 70 void Splay(int x){ 71 while(!tr[x].is_root){ 72 int y = tr[x].pre, z = tr[y].pre; 73 if(!tr[y].is_root){ 74 if((y == tr[z].son[1]) != ( x == tr[y].son[1])) rotate(y); 75 else rotate(x); 76 } 77 rotate(x); 78 } 79 Push_Up(x); 80 } 81 void access(int x){ 82 int y = 0; 83 do{ 84 Splay(x); 85 tr[rch].is_root = 1; 86 rch = y; 87 tr[rch].is_root = 0; 88 Push_Up(x); 89 y = x; 90 x = tr[x].pre; 91 }while(x); 92 } 93 inline void link(int u, int v){ 94 if(v > n) v = 0; 95 tr[u].pre = v; 96 } 97 inline void cut(int x){ 98 access(x); 99 Splay(x); 100 tr[lch].is_root = 1; 101 tr[lch].pre = 0; 102 lch = 0; 103 Push_Up(x); 104 } 105 inline int Query(int x){ 106 access(x); 107 Splay(x); 108 return tr[lch].sz + 1; 109 } 110 int main(){ 111 int t, x; 112 scanf("%d", &t); 113 while(t--){ 114 scanf("%d", &n); 115 tot = 0; 116 memset(head, -1, sizeof(int)*(n+1)); 117 tr[1].init(); 118 for(int i = 2; i <= n; i++){ 119 tr[i].init(); 120 scanf("%d", &x); 121 add(x, i); 122 } 123 dfs(1); 124 for(int i = 1; i <= n; i++){ 125 scanf("%d", &x); 126 link(i, Find(i,x)); 127 } 128 scanf("%d", &m); 129 int op, k; 130 while(m--){ 131 scanf("%d", &op); 132 if(op == 1) { 133 scanf("%d", &x); 134 printf("%d\n", Query(x)); 135 } 136 else { 137 scanf("%d%d", &x, &k); 138 cut(x); 139 link(x, Find(x,k)); 140 } 141 } 142 } 143 return 0; 144 }
虽然dls说分块和lct复杂度差不多, 但是这2份代码相比较lct快了100多, 实际上我觉得树上分块比较玄学。