树链剖分

什么是树链剖分

树链剖分并不是一个复杂的算法或者数据结构,它能把一棵树拆成链。

树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。

给定一棵树,将它划分成若干条互不相交的路径,满足:从节点 u->v 最多经过 logn 条路径以及 logn 条不在路径上的边。

树链剖分后,我们就可以利用其它的数据结构对在一棵树上进行路径的修改、求极值、求和的一类问题进行求解了。

 

一些定义

重儿子:以结点的一个儿子为根的子树的结点个数中最大的那一个儿子称为重儿子。

轻儿子:除了重儿子以外的儿子。

重边:结点与其重儿子的边称为重边。

轻边:结点与其轻儿子的边称为轻边。

重链:由重边组成的路径。

轻链:由轻边组成的路径。

 

描述剖分后的树

描述结点:

siz[v]:以结点v为根的子树的结点个数

dep[v]:结点v的深度,定义根结点的深度为0

fa[v]:结点v的父亲结点

 

描述结点与路径之间的关系:

belong[v]:结点v所属的路径编号

idx[v]:结点v在其路径中的编号

son[v]:结点v的重儿子

 

描述路径:

top[p]:编号为p的路径的顶端结点

len[p]:路径p的长度

 

描述辅助数据结构:

sump[p]:路径p的编号

seg[v]:结点v的父边在线段树中的位置

wei[v]:结点v的父边的权值

 

剖分后的性质

如果 (u,v) 为轻边,则 siz[v] * 2 < siz[u],即以轻儿子为根的子树中的结点数量相对少。

从根到某一点的路径上轻链、重链的个数都不大于 logn。

 

树链剖分的实现

首先用一次搜索求出 dep、fa、wei 的值,深搜广搜都可以,但是深搜容易爆栈,这里使用广搜。

在广搜的过程中得到了树的拓扑序,我们按照拓扑序的逆序遍历所有的结点。

在这个过程中可以求出 siz 与 son。

对于一个结点,如果它是叶子结点,那么我们就新建一条链,使该结点作为链上的第一个结点;

如果不是叶子结点,那么它与它的重儿子属于同一条链,对链进行更新即可。

 1 void split(){

 2     memset(dep,-1,sizeof(dep));

 3     l=0;

 4     dep[ que[r=1]=1 ]=0; // 将根结点插入队列,并设深度为0

 5     fa[1]=-1; // 默认 1 为根结点

 6     wei[1]=0;

 7     while (l<r){ // 第一遍搜索求出 fa,dep,wei

 8         int u=que[++l];

 9         vis[u]=false; // 顺便初始化vis

10         for (int i=head[u];i!=-1;i=edges[i].next){

11             int v=edges[i].to;

12             int w=edges[i].w;

13             if (dep[v]==-1){ // 未访问过的结点

14                 dep[ que[++r]=v ]=dep[u]+1; // 将v插入队列并设深度为dep[u]+1

15                 fa[v]=u; // v的父结点为u

16                 wei[v]=w; // v的父边权值

17             }

18         }

19     }

20     cnt=0; // 重链编号

21     for (int i=n;i>0;i--){

22         int u=que[i],p=-1;

23         siz[u]=1;

24         son[u]=p;

25         for (int k=head[u];k!=-1;k=edges[k].next){

26             int v=edges[k].to;

27             if (vis[v]){ // 若v是u的子结点

28                 siz[u]+=siz[v]; // 计数

29                 if (p==-1||siz[v]>siz[p]){

30                     son[u]=v;

31                     p=v; // u的重儿子是v

32                 }

33             }

34         }

35         if (p==-1){ // u是叶子结点

36             idx[u]=len[++cnt]=1; // 一个新的路径编号为cnt,u是路径中的第一个结点

37             belong[ top[cnt]=u ]=cnt; // u是顶端结点,且u属于路径cnt

38         }

39         else{ // u不是叶子结点

40             idx[u]=++len[ belong[u]=belong[p] ]; // u属于重儿子所在的链,链长+1,u是路径中第len个结点

41             top[ belong[u] ]=u; // u是顶端结点

42         }

43         vis[u]=true; // 访问标记

44     }

45 }
树链剖分

 

路径上的求和、求极值操作

我们对树上的边进行编号,编号对应着该边在线段树上的位置,保证同一个重链上的所有点的父边的编号是连续的即可。

这样我们就可以通过对线段树进行区间操作来快速的修改同一条重链上的权值了。

如图所示:

树链剖分

如何快速的处理任意两个结点 (va,vb) 间的边呢。

我们设 f1=top[belong[va]],f2=top[belong[vb]]。表示 f1 是 va 所属的链上的最顶端的点, f2 是 vb 所属的链上的最顶端的点。

当 f1 != f2 时,若 dep[f1] >= dep[f2],那么就处理 va 的父边到 f1 父边的路径,由于它们属于同一条重链的父边,编号是连续的,因此可以用线段树进行区间处理,最后使 va = fa[f1],重复进行该步骤直到 f1=f2。

当 f1 = f2 时,va 与 vb 在同一条重链上,若 va 与 vb 不是同一点,就区间处理 va 到 vb 路径上的边,否则修改完成。

查询操作与修改操作是类似的。

例如要查找两个结点间路径上的权值最大的边:

 1 int find(int va,int vb){

 2     int f1=top[belong[va]],f2=top[belong[vb]],tmp=0;

 3     while (f1!=f2){

 4         if (dep[f1]<dep[f2]){

 5             swap(f1,f2);

 6             swap(va,vb);

 7         }

 8         tmp=max(tmp,tr.query(1,seg[f1],seg[va]));

 9         va=fa[f1];

10         f1=top[belong[va]];

11     }

12     if (va==vb) return tmp;

13     if (dep[va]>dep[vb]) swap(va,vb);

14     return max(tmp,tr.query(1,seg[son[va]],seg[vb]));

15 }
查找路径上边权最大的边

 

相关练习题

SPOJ 375. Query on a tree

题目给出一棵含有n个结点的树,n-1条边每条边都有边权,有两种操作,修改第i条边的边权,查询两个结点的路径上最大的边权。

首先进行树链剖分,对边进行编号,建立线段树储存权值。

对于修改操作,我们找到该边在线段树上的位置然后单点更新即可。

对于查询操作,利用重链上的父边编号相同这一性质不断进行区间查询,就能快速找到最大的边权。

 

POJ 3237 Tree

与上一题相比多了一种操作,将两个结点间的边权取负。

用线段树维护两个值,区间上的最小值与最大值。区间取负时将最大值最小值取负后交换,打上延迟标记。

  1 #include <iostream>

  2 #include <cstdio>

  3 #include <cstring>

  4 #include <algorithm>

  5 

  6 using namespace std;

  7 

  8 const int maxn=101010+5;

  9 const int maxm=maxn+maxn;

 10 

 11 struct EDGENODE{

 12     int to;

 13     int w;

 14     int next;

 15 }edges[maxm];

 16 int head[maxn],edge;

 17 inline void init(){

 18     edge=0;

 19     memset(head,-1,sizeof(head));

 20 }

 21 inline void addedge(int u,int v,int w){

 22     edges[edge].w=w,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;

 23     edges[edge].w=w,edges[edge].to=u,edges[edge].next=head[v],head[v]=edge++;

 24 }

 25 int que[maxn]; // 队列

 26 bool vis[maxn]; // 访问标记

 27 int son[maxn]; // 重儿子

 28 int idx[maxn]; // 结点v在其路径中的编号

 29 int dep[maxn]; // 结点v的深度

 30 int siz[maxn]; // 以结点v为根的子树的结点个数

 31 int belong[maxn]; // 结点v所属的路径编号

 32 int fa[maxn]; // 结点v的父亲结点

 33 int top[maxn]; // 编号为p的路径的顶端结点

 34 int len[maxn]; // 路径p的长度

 35 int sump[maxn]; // 路径p的编号

 36 int seg[maxn]; // 结点v的父边在线段树中的位置

 37 int wei[maxn]; // 结点v的父边的权值

 38 int l,r,ans,cnt;

 39 int n;

 40 char cmd[22];

 41 

 42 void split(){

 43     memset(dep,-1,sizeof(dep));

 44     l=0;

 45     dep[ que[r=1]=1 ]=0; // 将根结点插入队列,并设深度为0

 46     fa[1]=-1; // 默认 1 为根结点

 47     wei[1]=0;

 48     while (l<r){ // 第一遍搜索求出 fa,dep,wei

 49         int u=que[++l];

 50         vis[u]=false; // 顺便初始化vis

 51         for (int i=head[u];i!=-1;i=edges[i].next){

 52             int v=edges[i].to;

 53             int w=edges[i].w;

 54             if (dep[v]==-1){ // 未访问过的结点

 55                 dep[ que[++r]=v ]=dep[u]+1; // 将v插入队列并设深度为dep[u]+1

 56                 fa[v]=u; // v的父结点为u

 57                 wei[v]=w; // v的父边权值

 58             }

 59         }

 60     }

 61     cnt=0; // 重链编号

 62     for (int i=n;i>0;i--){

 63         int u=que[i],p=-1;

 64         siz[u]=1;

 65         son[u]=p;

 66         for (int k=head[u];k!=-1;k=edges[k].next){

 67             int v=edges[k].to;

 68             if (vis[v]){ // 若v是u的子结点

 69                 siz[u]+=siz[v]; // 计数

 70                 if (p==-1||siz[v]>siz[p]){

 71                     son[u]=v;

 72                     p=v; // u的重儿子是v

 73                 }

 74             }

 75         }

 76         if (p==-1){ // u是叶子结点

 77             idx[u]=len[++cnt]=1; // 一个新的路径编号为cnt,u是路径中的第一个结点

 78             belong[ top[cnt]=u ]=cnt; // u是顶端结点,且u属于路径cnt

 79         }

 80         else{ // u不是叶子结点

 81             idx[u]=++len[ belong[u]=belong[p] ]; // u属于重儿子所在的链,链长+1,u是路径中第len个结点

 82             top[ belong[u] ]=u; // u是顶端结点

 83         }

 84         vis[u]=true; // 访问标记

 85     }

 86 }

 87 const int INF=0x3fffffff;

 88 struct SegmentTree{

 89     int num[maxn];

 90     struct Tree{

 91         int l;

 92         int r;

 93         int max;

 94         int min;

 95         bool neg;

 96     };

 97     Tree tree[maxn*4];

 98     void push_down(int root){

 99         if (tree[root].neg){

100             if (tree[root].l!=tree[root].r){

101                 tree[root<<1].neg^=1;

102                 tree[root<<1|1].neg^=1;

103                 swap(tree[root<<1].max,tree[root<<1].min);

104                 swap(tree[root<<1|1].max,tree[root<<1|1].min);

105                 tree[root<<1].max*=-1;

106                 tree[root<<1].min*=-1;

107                 tree[root<<1|1].max*=-1;

108                 tree[root<<1|1].min*=-1;

109             }

110         }

111         tree[root].neg=0;

112     }

113     void push_up(int root){

114         tree[root].max=max(tree[root<<1].max,tree[root<<1|1].max);

115         tree[root].min=min(tree[root<<1].min,tree[root<<1|1].min);

116     }

117     void build(int root,int l,int r){

118         tree[root].l=l;

119         tree[root].r=r;

120         tree[root].neg=0;

121         if(tree[root].l==tree[root].r){

122             tree[root].max=num[l];

123             tree[root].min=num[l];

124             tree[root].neg=0;

125             return;

126         }

127         int mid=(l+r)/2;

128         build(root<<1,l,mid);

129         build(root<<1|1,mid+1,r);

130         push_up(root);

131     }

132     void update(int root,int pos,int val){

133         if(tree[root].l==tree[root].r){

134             tree[root].max=val;

135             tree[root].min=val;

136             return;

137         }

138         push_down(root);

139         int mid=(tree[root].l+tree[root].r)/2;

140         if(pos<=mid) update(root<<1,pos,val);

141         else update(root<<1|1,pos,val);

142         push_up(root);

143     }

144     int query(int root,int L,int R){

145         if(L<=tree[root].l&&R>=tree[root].r) return tree[root].max;

146         push_down(root);

147         int mid=(tree[root].l+tree[root].r)/2,ret=-INF;

148         if(L<=mid) ret=max(ret,query(root<<1,L,R));

149         if(R>mid) ret=max(ret,query(root<<1|1,L,R));

150         push_up(root);

151         return ret;

152     }

153     void nega(int root,int L,int R){

154         if (L<=tree[root].l&&R>=tree[root].r){

155             tree[root].neg^=1;

156             swap(tree[root].max,tree[root].min);

157             tree[root].max*=-1;

158             tree[root].min*=-1;

159             return;

160         }

161         push_down(root);

162         int mid=(tree[root].l+tree[root].r)/2;

163         if (L<=mid) nega(root<<1,L,R);

164         if (R>mid) nega(root<<1|1,L,R);

165         push_up(root);

166     }

167     void debug(int root){

168         printf("rt=%d, [%d~%d], min=%d, max=%d, neg=%d\n",root,tree[root].l,tree[root].r,tree[root].min,tree[root].max,(int)tree[root].neg);

169         if (tree[root].l==tree[root].r) return;

170         debug(root<<1);

171         debug(root<<1|1);

172     }

173 }tr;

174 

175 int find(int va,int vb){

176     int f1=top[belong[va]],f2=top[belong[vb]],tmp=-INF;

177     while (f1!=f2){

178         if (dep[f1]<dep[f2]){

179             swap(f1,f2);

180             swap(va,vb);

181         }

182         tmp=max(tmp,tr.query(1,seg[f1],seg[va]));

183         va=fa[f1];

184         f1=top[belong[va]];

185     }

186     if (va==vb) return tmp;

187     if (dep[va]>dep[vb]) swap(va,vb);

188     return max(tmp,tr.query(1,seg[son[va]],seg[vb]));

189 }

190 void gao(int va,int vb){

191     int f1=top[belong[va]],f2=top[belong[vb]];

192     while (f1!=f2){

193         if (dep[f1]<dep[f2]){

194             swap(f1,f2);

195             swap(va,vb);

196         }

197         tr.nega(1,seg[f1],seg[va]);

198         va=fa[f1];

199         f1=top[belong[va]];

200     }

201     if (va==vb) return;

202     if (dep[va]>dep[vb]) swap(va,vb);

203     tr.nega(1,seg[son[va]],seg[vb]);

204 }

205 int d[maxn][3];

206 

207 int main()

208 {

209     int T;

210     scanf("%d",&T);

211     while (T--){

212         init();

213         scanf("%d",&n);

214         for (int i=1;i<n;i++){

215             int a,b,c;

216             scanf("%d%d%d",&a,&b,&c);

217             d[i][0]=a;

218             d[i][1]=b;

219             d[i][2]=c;

220             addedge(a,b,c);

221         }

222         split();

223         sump[0]=0;

224         for (int i=1;i<=cnt;i++) sump[i]=sump[i-1]+len[i];

225         for (int i=1;i<=n;i++){

226             seg[i]=sump[ belong[i] ]-idx[i]+1;

227             tr.num[ seg[i] ]=wei[i];

228         }

229         tr.build(1,1,n);

230         while (scanf("%s",cmd)){

231             if (cmd[0]=='D') break;

232             int x,y;

233             scanf("%d%d",&x,&y);

234             if (cmd[0]=='Q'){

235                 printf("%d\n",find(x,y));

236             }

237             if (cmd[0]=='C'){

238                 if (fa[d[x][1]]==d[x][0]) tr.update(1,seg[d[x][1]],y);

239                 else tr.update(1,seg[d[x][0]],y);

240             }

241             if (cmd[0]=='N'){

242                 gao(x,y);

243             }

244         }

245     }

246     return 0;

247 }

248 

249 /**

250 1

251 10

252 1 2 1

253 2 3 3

254 3 4 8

255 4 5 -6

256 5 6 -9

257 6 7 -1

258 7 8 1

259 8 9 7

260 9 10 6

261 NEGATE 1 8

262 CHANGE 2 7

263 QUERY 1 3

264 NEGATE 1 5

265 CHANGE 4 7

266 QUERY 3 5

267 DONE

268 

269 3

270 10

271 1 2 1

272 2 3 7

273 3 4 8

274 4 5 6

275 5 6 9

276 6 7 1

277 7 8 1

278 8 9 7

279 9 10 6

280 NEGATE 4 7

281 CHANGE 2 3

282 QUERY 2 9

283 CHANGE 2 3

284 QUERY 6 8

285 NEGATE 1 8

286 CHANGE 2 7

287 QUERY 1 3

288 NEGATE 1 5

289 CHANGE 4 7

290 QUERY 3 5

291 DONE

292 6

293 1 2 1

294 2 3 2

295 3 4 4

296 4 5 100

297 5 6 -1000

298 QUERY 1 2

299 CHANGE 1 3

300 QUERY 1 2

301 NEGATE 1 2

302 QUERY 1 3

303 QUERY 1 2

304 CHANGE 1 10

305 QUERY 1 3

306 NEGATE 1 2

307 QUERY 1 3

308 CHANGE 1 10

309 CHANGE 2 20

310 QUERY 1 3

311 NEGATE 1 2

312 QUERY 1 3

313 NEGATE 2 3

314 QUERY 1 3

315 CHANGE 1 -100

316 CHANGE 2 -1000

317 QUERY 1 4

318 NEGATE 1 6

319 QUERY 1 6

320 DONE

321 100

322 1 2 265

323 2 3 133

324 3 4 508

325 4 5 197

326 5 6 437

327 6 7 849

328 7 8 577

329 8 9 503

330 9 10 478

331 10 11 434

332 11 12 877

333 12 13 691

334 13 14 54

335 14 15 295

336 15 16 421

337 16 17 166

338 17 18 550

339 18 19 410

340 19 20 868

341 20 21 476

342 21 22 283

343 22 23 410

344 23 24 915

345 24 25 308

346 25 26 301

347 26 27 553

348 27 28 609

349 28 29 733

350 29 30 770

351 30 31 635

352 31 32 581

353 32 33 753

354 33 34 707

355 34 35 448

356 35 36 738

357 36 37 841

358 37 38 389

359 38 39 532

360 39 40 210

361 40 41 458

362 41 42 595

363 42 43 989

364 43 44 678

365 44 45 214

366 45 46 746

367 46 47 548

368 47 48 117

369 48 49 758

370 49 50 437

371 50 51 840

372 51 52 555

373 52 53 726

374 53 54 490

375 54 55 719

376 55 56 403

377 56 57 329

378 57 58 92

379 58 59 311

380 59 60 664

381 60 61 207

382 61 62 170

383 62 63 548

384 63 64 713

385 64 65 556

386 65 66 705

387 66 67 82

388 67 68 508

389 68 69 59

390 69 70 45

391 70 71 670

392 71 72 540

393 72 73 826

394 73 74 262

395 74 75 504

396 75 76 989

397 76 77 408

398 77 78 896

399 78 79 388

400 79 80 15

401 80 81 485

402 81 82 219

403 82 83 977

404 83 84 641

405 84 85 985

406 85 86 189

407 86 87 64

408 87 88 641

409 88 89 320

410 89 90 788

411 90 91 441

412 91 92 785

413 92 93 163

414 93 94 153

415 94 95 852

416 95 96 36

417 96 97 10

418 97 98 145

419 98 99 956

420 99 100 641

421 QUERY 32 69

422 NEGATE 1 22

423 CHANGE 40 53

424 QUERY 17 38

425 NEGATE 17 65

426 CHANGE 49 68

427 QUERY 44 52

428 NEGATE 11 53

429 CHANGE 9 68

430 QUERY 2 49

431 NEGATE 25 45

432 CHANGE 23 67

433 QUERY 89 90

434 NEGATE 5 37

435 CHANGE 27 53

436 QUERY 22 86

437 NEGATE 6 7

438 CHANGE 17 23

439 QUERY 78 93

440 NEGATE 30 63

441 CHANGE 56 99

442 QUERY 3 29

443 NEGATE 24 38

444 CHANGE 9 95

445 QUERY 63 66

446 NEGATE 69 92

447 CHANGE 9 91

448 QUERY 7 27

449 NEGATE 32 60

450 CHANGE 48 77

451 QUERY 47 94

452 NEGATE 14 27

453 CHANGE 50 99

454 QUERY 38 97

455 NEGATE 11 67

456 CHANGE 74 83

457 QUERY 28 81

458 NEGATE 13 53

459 CHANGE 55 88

460 QUERY 2 66

461 NEGATE 71 95

462 CHANGE 32 74

463 QUERY 14 50

464 NEGATE 1 28

465 CHANGE 16 80

466 QUERY 36 75

467 NEGATE 20 49

468 CHANGE 22 54

469 QUERY 5 46

470 NEGATE 12 37

471 CHANGE 61 94

472 QUERY 18 92

473 NEGATE 19 26

474 CHANGE 6 94

475 QUERY 33 60

476 NEGATE 79 87

477 CHANGE 30 75

478 QUERY 55 94

479 NEGATE 28 79

480 CHANGE 23 31

481 QUERY 91 95

482 NEGATE 28 76

483 CHANGE 8 41

484 QUERY 6 25

485 NEGATE 19 70

486 CHANGE 17 54

487 QUERY 52 66

488 NEGATE 4 95

489 CHANGE 19 52

490 QUERY 73 87

491 DONE

492 

493 

494 **/
POJ 3237 Tree

 

你可能感兴趣的:(树)