void dfs(int u,int f){
for(auto v:mp[u]){
if(v==f)continue;//防止重复访问
dfs(v,u);
}
}
一般有两种dfs序,通过长度进行分类:
int in[N],seq[N],out[N],idc;
void dfs(int u,int f){
seq[++idc] = u;
in[u] = idc;
for(auto v:mp[u]){
if(v==f)continue;
dfs(v,u);
}
out[u] = idc;
}
idc = 0;
dfs(root,root);
每个节点和 DFS 序中的位置一一对应,即 u 所在序列中位置是 in[u],
序列下标 i 对应节点是 seq[i] ,
所以有 seq[in[u]] = u
每棵子树在 DFS 序中是连续的,即 u 节点代表的子树的所有节点
都在 [in[u],out[u]] 中。
int in[N],seq[N],out[N],idc;
void dfs(int u,int f){
seq[++idc] = u;
in[u] = idc;
for(auto v:mp[u]){
if(v==f)continue;
dep[v]=dep[u]+1;
dfs(v,u);
seq[++idc] = u;
}
}
idc = 0;
dfs(root,root);
可以看成将每条边放入队列里两次,然后在最初根会被多算一次,所以最后 idc = 2*m+1 , 而树的边数与点数有 m = n-1 所以 idc = 2*n-1
u和v的lca即是[in[u],in[v]]这一段中的点的深度最浅的那个
描述:树的每个结点都有权值。两个操作:单点修改结点权值,查询子树权值之和.
按照第一种方法建立子树深搜序,那么 [ i n [ u ] , o u t [ u ] ] [in[u],out[u]] [in[u],out[u]] 就表示以 u u u 为根节点的子树对应的深搜序. 这样子的话,就可以用树状数组维护。
描述:树的每个节点都有颜色。两个操作:单点修改结点颜色,查询子树颜色种类数量
按照第一种方法建立树的深搜序,把序列分成一段一段的,每段的颜色都相同。建立深搜序列的树状数组。对于每一段颜色,每个点加1,相邻两个结点的最近公共祖先减1.
查询以 u u u 为根的子树颜色种类数量,仍然是 [ i n [ u ] , o u t [ u ] ] [in[u],out[u]] [in[u],out[u]] 的区间求和.
描述:树的每个节点都有权值。单点修改结点权值,查询结点到根的路径权值之和.
按照第一种方法建立树的深搜序,树状数组维护深搜序,到根节点路径之和的差分数组。那么它影响的,是以 u u u 为根节点的子树的答案,子树所有节点到根节点路径之和都增加了 Δ w [ u ] \Delta w[u] Δw[u],即 u u u 权值的改变量。修改结点 u u u 时, a d d ( i n [ u ] , d ) , a d d ( o u t [ u ] + 1 , − d ) add(in[u], d),add(out[u] + 1,-d) add(in[u],d),add(out[u]+1,−d).
查询 u u u 到根节点路径之和时,即 s u m ( i n [ u ] ) sum(in[u]) sum(in[u]) 即可.
查询任意两节点的路径权值之和。
在深搜的时候,建立第一种深搜序,并且建立权值可持久化线段树,每一个子结点 v v v 的版本信息从父结点 u u u 更新而来。查询结点 u u u 的时候,只需要查询 r o o t [ u ] root[u] root[u] 对应的线段树即可
建立第一种深搜序,建立可持久化权值线段树,每一个结点 u u u 版本信息从深搜序为 i n [ u ] − 1 in[u] - 1 in[u]−1 的线段树更新而来。查询的时候,查询 r o o t [ o u t [ u ] ] − r o o t [ i n [ u ] − 1 ] root[out[u]] - root[in[u]-1] root[out[u]]−root[in[u]−1] 对应的线段树即可。
查询在以 u u u 为根的子树中,到达 u u u 距离不超过 k k k 的结点中结点权值最小值。
方法:建立一个按照深度为序的树序列(可以理解为层次优先遍历),并且标记第一种深搜序。建立一个可持久化权值线段树,每一层作为一个版本信息,从上一层的版本更新而来.
线段树维护的序列就是深搜序的序列,把 i n [ u ] in[u] in[u] 对应的线段树结点权值修改为 u u u 对应的点权。比如编号为 x x x 的节点,就要把 r o o t [ d e p [ x ] ] root[dep[x]] root[dep[x]] 版本的线段树的 i n [ x ] in[x] in[x] 对应的结点权值修改为 w [ x ] w[x] w[x]。这样子查询 u u u 的时候,只需查询 r o o t [ d e p [ u ] ] root[dep[u]] root[dep[u]] 在区间 [ i n [ u ] , o u t [ u ] ] [in[u],out[u]] [in[u],out[u]] 的最小值即可。因为深度大于 d e p [ u ] dep[u] dep[u] 的结点的信息不会存在于 r o o t [ d e p [ u ] ] root[dep[u]] root[dep[u]] 里面,而深度小于 d e p [ u ] dep[u] dep[u] 节点信息不会存在于区间 [ i n [ u ] , o u t [ u ] ] [in[u],out[u]] [in[u],out[u]] 之中。
例:
F. Subtree Minimum Query
题意:给一棵树,每个节点有点权,强制在线询问:给定节点 u u u,问在以 u u u 为根的子树中,到 u u u 距离不超过 k k k 的点中,点权最小值是多少?
#include
using namespace std;
const int N = 100010, M = N * 2;
const int INF = 1e9+7;
int h[N], e[M], ne[M], nidx;
int w[N];
int n, rt, m;
inline void add(int a, int b)
{
e[nidx] = b, ne[nidx] = h[a], h[a] = nidx++;
}
int dep[N], in[N], out[N], cnt, max_dep;
vector<int> dep_order[N];
void dfs(int u, int fa)
{
//printf("Im here\n");
dep[u] = dep[fa] + 1;
dep_order[dep[u]].push_back(u);
max_dep = max(max_dep, dep[u]);
in[u] = ++cnt;
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
dfs(v, u);
}
out[u] = cnt;
}
struct node
{
int l, r;
int mmin;
}tr[N * 25];
int root[N], idx;
void pushup(int p)
{
tr[p].mmin = min(tr[tr[p].l].mmin, tr[tr[p].r].mmin);
}
int build(int l, int r)
{
//注意 build 的时候把 mmin 设成 INF
int p = ++idx;
if(l == r)
{
tr[p].mmin = INF;
return p;
}
int mid = l + r >> 1;
tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r);
pushup(p);
return p;
}
int insert(int p, int l, int r, int id, int x)
{
int q = ++idx;
tr[q] = tr[p];
if(l == r)
{
tr[q].mmin = x;
return q;
}
int mid = l + r >> 1;
if(id <= mid) tr[q].l = insert(tr[p].l, l, mid, id, x);
else tr[q].r = insert(tr[p].r, mid + 1, r, id, x);
pushup(q);
return q;
}
int query(int p, int l, int r, int L, int R)
{
if(L <= l && r <= R) return tr[p].mmin;
int mid = l + r >> 1;
int res = 1e9;
if(L <= mid) res = query(tr[p].l, l, mid, L, R);
if(R > mid) res = min(res, query(tr[p].r, mid + 1, r, L, R));
return res;
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d", &n, &rt);
for(int i = 1; i <= n; i++)
{
scanf("%d", &w[i]);
}
for(int i = 1; i < n; i++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(rt, 0);
root[0] = build(1, n);
for(int i = 1; i <= max_dep; i++)
{
root[i] = root[i - 1];
for(auto u : dep_order[i])
{
//insert 里面别写成 root[i - 1]
root[i] = insert(root[i], 1, n, in[u], w[u]);
}
}
scanf("%d", &m);
int ans = 0;
for(int i = 1; i <= m; i++)
{
int u, k;
scanf("%d%d", &u, &k);
u = (u + ans) % n + 1, k = (k + ans) % n;
//printf("** k = %d u = %d, in[u] = %d, out[u] = %d, dep[u] = %d\n", k, u, in[u], out[u], dep[u]);
ans = query(root[min(max_dep, dep[u] + k)], 1, n, in[u], out[u]);
printf("%d\n", ans);
}
return 0;
}