(update:2012.11.22)
【这绝对是个坑啊!!!】
资料:http://pan.baidu.com/share/link?shareid=116174&uk=3491581918
1. 基础知识
a) 树状数组、线段树、堆、可并堆、Trie
b) 平衡树(splay、treap/sbt)
c) LCA、RMQ、LCT、树链剖分
2. 树上的修改、询问
a) 修改点权,问路径上的点权和、子树点权和
b) 修改路径上的点(每个点点权同时加减定制),询问子树点权和、单点点权
c) 修改子树点权(每个点点权同时加减定制),询问路径点权和,单点点权
d) 修改子树点权(每个点点权同时加减定制),询问子树点权和
e) 将路径上所有点点权设置为w,查询路径上边权组成的序列的最大子段和
f) Qtree系列
i. 修改边权问路径上最大边,n<=10^4.
ii. 问路径上的k短边,n<=10^4.
iii. (1)对一棵被黑白染色的树,支持对于某个节点反色、查询节点1 到i路径上第一个黑色节点的编号。
(2)给定一棵有根树,树根为1,询问某子树中k大权值的节点是哪个,
(1)、(2)范围均为n、q<=10^5
iv. 对一棵被黑白染色的树,支持对于某个节点反色、查询数中最远的两个白色节点的距离。n、q<=10^5
v. 对一棵被黑白染色的树,初始时所有节点均为黑色,要求支持对于某个节点反色、查询节点1 到i路径上第一个黑色节点的编号。查询距离i最远的白色节点的编号。
n、q<=10^5
附 :Qtree Links:
1. http://www.spoj.pl/problems/QTREE/
2. http://www.spoj.pl/problems/QTREE2/
3.1 http://www.spoj.pl/problems/QTREE3/
3.2 https://www.spoj.pl/problems/PT07J/
4. http://www.spoj.pl/problems/QTREE4/
5. http://www.spoj.pl/problems/QTREE5/
3. 树形DP
a) 树形依赖背包、泛化物品 详见背包九讲。
b) LJQ树的难题、DP合集、分类练习题、自己找题。
c) Trie、ACAutomatic 上的DP
4. 生成树
a) 基础算法
b) 次小、最优比例(01分数规划)、度限制、生成树计数(生成树的计数及其应用(ZD))
5. Link-Cut Tree介绍
其实我也是最近才开始看......
这个东西真是让人欢喜让人忧...欢喜在于这东西真是太好用了..忧在于总是难以弄清细节..至今都不是很懂。
实际上LCT还是蛮好写的。只是常数嘛...不过据说可以用全局平衡二叉树来优化。但是又是一段代码。
具体的可以看杨哲的论文和FHQ的博客。论文在资料中。
弹飞绵羊的code:(只会最基础的求破...)
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<windows.h>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<climits>
#define isr(x) ((x) -> f -> c[1] == (x))
#define isl(x) ((x) -> f -> c[0] == (x))
#define setc(F,d,C) (((F) -> c[d] = (C)) -> f = (F))
#define maxm 200010
#define maxn 200005
#define ot "%d"
#define kh "\n"
#define top(x) ((x) -> f == null || ((x) != (x) -> f -> c[0] && (x) != (x) -> f -> c[1]))
using namespace std;
struct node {int s; node *c[2], *f;} *null, *a[maxn], *head, *tail, vess[maxm];
typedef node *NODE;
int pop = maxn - 1, n, q, x, y;
void prepare()
{
null = &vess[maxm - 1], null -> s = 0, null -> f = null;
for (int i = 0; i < maxn; ++i) a[i] = &vess[i], vess[i] = (node){1, {null, null}, null};
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
}
NODE update(NODE x)
{
if (x == null) return null;
x -> s = x -> c[0] -> s + x -> c[1] -> s + 1;
return x;
}
void rotate(NODE x)
{
NODE y = x -> f, z = y -> f; bool k = isl(x);
if (x -> c[k] != null)
setc(y, !k, x -> c[k]);
else
y -> c[!k] = null;
if (isl(y)) z -> c[0] = x;
if (isr(y)) z -> c[1] = x;
x -> f = z; setc(x, k, update(y));
}
NODE splay(NODE x)
{
for (; !top(x); rotate(x))
if (!top(x -> f))
rotate((isr(x) ^ isr(x -> f)) ? x : x -> f);
return update(x);
}
NODE access(NODE x)
{
NODE y = null;
while (x != null)
splay(x) -> c[1] = y, y = update(x), x = x -> f;
return y;
}
int main()
{
prepare();
scanf(ot, &n);
int k;
for (int i = 0; i < n; ++i)
{
scanf(ot, &k);
a[i] -> f = a[i + k > n ? n : i + k];
}
for (scanf(ot, &q); q; --q)
{
scanf(ot, &k);
if (k == 1) scanf(ot, &x), printf(ot kh, access(a[x]) -> s - 1);
else
{
scanf(ot ot, &x, &y);
splay(a[x]) -> c[0] -> f = a[x] -> f, a[x] -> c[0] = null;
update(a[x]) -> f = a[x + y > n ? n : x + y];
}
}
return 0;
}
6. Solution For 2
a) 修改点权,问路径上的点权和、子树点权和
i. 询问子树的点权和:
我们通过DFS序转化为区间求和即可。树状数组或线段树实现
ii 询问路径上的点权和
转化为点事件。在DFS序中,X为根的子树是一段区间[L, R], 我们将D[L]加上改变值,将D[R + 1]减去改变值,再次转化为区间求和。
b) 修改路径上的点(每个点点权同时加减定制),询问子树点权和、单点点权
i. 询问单点点权:转化为单点修改、查询子树和。
ii 询问子树点权和:转化为单点修改、查询路径和。考察贡献,变量分离,树状数组维护。首先把修改等价转化为修改X到1的路径上所有点的权值。然后同样考虑修改X对查询Y的贡献。显然当X在Y的子树内时候才会产生贡献,且贡献为W ( X ) * ( depth[ X] - depth [ Y ] + 1 ),分离变量得W ( X ) * ( depth[ X ] + 1 ) - W ( X ) * depth [ Y ]。前一项与Y无关,于是转化为问题2,可以用一个树状数组维护,后一项里W(X)与Y无关,也转化为问题2,用一个树状数组维护,有了W ( X ) * ( depth [ X ] + 1 )和W(X)我们就可以计算出询问的答案。
c) 修改子树点权(每个点点权同时加减定制),询问路径点权和,单点点权
i. 询问单点点权。点事件。
ii. 询问路径。LCT或变量分离。
d) 修改子树点权(每个点点权同时加减定制),询问子树点权和。点事件。
e) 将路径上所有点点权设置为w,查询路径上边权组成的序列的最大子段和