目录
DFS序:
POJ3321 Apple Tree
HDU3887 Counting Offspring
CF620E New Year Tree
CF383C Propagating tree
树链剖分:
POJ2763 Housewife Wind
HDU2856 How far away ?
CF343D Water Tree
POJ3237 Tree
HDU3966 Aragorn's Story
题意:有一颗苹果树,苹果会长在分叉处,若对某一分叉进行操作:原本有苹果则摘下苹果,否则长一颗苹果。对于每一个询问输出分叉X已经它上面有多少个苹果。
思路:线段树区间求和+单点修改。用DFS序将树转为线性结构,则分叉X上方的分叉区间为:in[x] ~ out[x]。
AC代码:
/*---------------------------------
*File name: A-dfs序+线段树.cpp
*Creation date: 2020-06-30 13:35
*Link: http://poj.org/problem?id=3321
*-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
struct Edge{
int v, nxt;
}edge[maxn << 1];
struct Tree{
int l, r, sum;
}t[maxn << 2];
int head[maxn];
int tot = 0;
int n, m;
int a[maxn], Time;
int in[maxn], out[maxn];
inline void Add_Edge(int u, int v){
edge[tot].v = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
void Dfs(int x, int pre){
in[x] = ++Time;
a[Time] = x;
for(int i = head[x]; i != -1; i = edge[i].nxt){
int v = edge[i].v;
if(v == pre) continue;
Dfs(v, x);
}
out[x] = Time;
}
void Build(int l, int r, int k){
t[k].l = l, t[k].r = r;
if(l == r){
t[k].sum = 1;
return;
}
int mid = l + r >> 1;
Build(l, mid, k << 1);
Build(mid + 1, r, k << 1 | 1);
t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}
int Query(int l, int r, int k, int L, int R){
if(L <= l && r <= R) return t[k].sum;
int mid = l + r >> 1;
int ans = 0;
if(L <= mid) ans += Query(l, mid, k << 1, L, R);
if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
return ans;
}
void Update(int l, int r, int k, int x){
if(l == r){
t[k].sum ^= 1;
return;
}
int mid = l + r >> 1;
if(x <= mid) Update(l, mid, k << 1, x);
else Update(mid + 1, r, k << 1 | 1, x);
t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}
int main(){
memset(head, -1, sizeof head);
scanf("%d", &n);
for(int i = 1; i < n; ++i){
int u, v;
scanf("%d %d", &u, &v);
Add_Edge(u, v);
Add_Edge(v, u);
}
Dfs(1, -1);
Build(1, n, 1);
scanf("%d", &m);
while(m--){
char op;
int x;
scanf(" %c %d", &op, &x);
if(op == 'Q'){
int l = in[x], r = out[x];
printf("%d\n", Query(1, n, 1, l, r));
}
else{
x = in[x];
Update(1, n, 1, x);
}
}
return 0;
}
题意:给一颗树,定义函数F( i )为结点 i 的子树中比它小的结点数。输出每个结点的F( i )。
思路:DFS处理原数后用树状数组或线段树处理。由小节点到大节点一个一个单点更新in[node[i]],在更新前计算子树中的区间和。
AC代码:
/*---------------------------------
*File name: C-Dfs序+线段树.cpp
*Creation date: 2020-06-30 15:33
*Link: http://acm.hdu.edu.cn/showproblem.php?pid=3887
*-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
struct Edge{
int v, next;
}edge[maxn << 1];
struct Tree{
int l, r;
int sum;
int f;
}t[maxn << 2];
int head[maxn];
int tot;
int Time, a[maxn];
int in[maxn], out[maxn];
inline void Add_Edge(int u, int v){
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void Dfs(int u, int pre){
in[u] = ++Time;
a[Time] = u;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == pre) continue;
Dfs(v, u);
}
out[u] = Time;
}
int node = 0;
void Build(int l, int r, int k){
t[k].l = l, t[k].r = r;
if(l == r){
t[k].sum = 0;
return;
}
int mid = l + r >> 1;
Build(l, mid, k << 1);
Build(mid + 1, r, k << 1 | 1);
t[k].sum = 0;
}
int Query(int l, int r, int k, int L, int R){
if(L <= l && r <= R) return t[k].sum;
int mid = l + r >> 1;
int ans = 0;
if(L <= mid) ans += Query(l, mid, k << 1 , L, R);
if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
return ans;
}
void Update(int l, int r, int k, int L, int R){
if(l == r){
t[k].sum += 1;
return;
}
int mid = l + r >> 1;
if(L <= mid) Update(l, mid, k << 1, L, R);
if(R > mid) Update(mid + 1, r, k << 1 | 1, L, R);
t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}
int main(){
int n, q;
while(scanf("%d %d", &n, &q) && n && q){
for(int i = 1; i <= n; ++i) head[i] = -1;
tot = 0, Time = 0;
for(int i = 1; i < n; ++i){
int u, v;
scanf("%d %d", &u, &v);
Add_Edge(u, v);
Add_Edge(v, u);
}
Dfs(q, -1);
Build(1, n, 1);
for(int i = 1; i <= n; ++i){
int l = in[i];
int r = out[i];
printf("%d%c", Query(1, n, 1, l, r), i == n ? '\n' : ' ');
Update(1, n, 1, l, l);
}
}
return 0;
}
题意:给一颗树,每个结点有初始颜色 ,有两种操作,第一种操作为将 已经它的子树全部涂成 。第二种操作为询问结点 的子树有多少种颜色。
思路:DFS将原树处理成线性结构后,用线段树处理操作,区间修改与区间查询。由于颜色种类数只有60种,所以用一个long long类型的数的二进制作为计算线段树覆盖区间内的不同颜色是否存在。
AC代码:
/*---------------------------------
*File name: E-DFS序+线段树.cpp
*Creation date: 2020-07-01 09:35
*Link:
*-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
struct Edge{
int v, next;
}edge[maxn << 1];
int head[maxn], tot;
struct Tree{
int l, r;
LL cnt;
int f;
}t[maxn << 2];
int in[maxn], out[maxn], Time, a[maxn];
int c[maxn];
inline void Add_Edge(int u, int v){
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void Dfs(int x, int pre){
in[x] = ++Time;
a[Time] = x;
for(int i = head[x]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == pre) continue;
Dfs(v, x);
}
out[x] = Time;
return;
}
int node;
void Build(int l, int r, int k){
t[k].l = l, t[k].r = r;
if(l == r){
t[k].cnt = (1LL << c[a[++node]]);
return;
}
int mid = l + r >> 1;
Build(l, mid, k << 1);
Build(mid + 1, r, k << 1 | 1);
t[k].cnt = (t[k << 1].cnt | t[k << 1 | 1].cnt);
}
inline void Down(Tree &lch, Tree &rch, int k){
lch.f = t[k].f;
lch.cnt = (1LL << t[k].f);
rch.f = t[k].f;
rch.cnt = (1LL << t[k].f);
t[k].f = 0;
}
void Update(int l, int r, int k, int L, int R, int x){
if(L <= l && r <= R){
t[k].cnt = (1ll << x);
t[k].f = x;
return;
}
if(t[k].f) Down(t[k << 1], t[k << 1 | 1], k);
int mid = l + r >> 1;
if(L <= mid) Update(l, mid, k << 1, L, R, x);
if(R > mid) Update(mid + 1, r , k << 1 | 1, L, R, x);
t[k].cnt = (t[k << 1].cnt | t[k << 1 | 1].cnt);
}
LL Query(int l, int r, int k, int L, int R){
if(L <= l && r <= R) return t[k].cnt;
if(t[k].f) Down(t[k << 1], t[k << 1 | 1], k);
int mid = l + r >> 1;
LL ans = 0;
if(L <= mid) ans |= Query(l, mid, k << 1, L, R);
if(R > mid) ans |= Query(mid + 1, r, k << 1 | 1, L, R);
return ans;
}
int cal(LL x){
int cnt = 0;
while(x) cnt += (x & 1), x >>= 1;
return cnt;
}
int main(){
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", &c[i]), head[i] = -1;
for(int i = 1; i < n; ++i){
int u, v;
scanf("%d %d", &u, &v);
Add_Edge(u, v);
Add_Edge(v, u);
}
Dfs(1, -1);
Build(1, n, 1);
//for(int i = 1; i <= n; ++i) printf("%d: %d %d\n", i, in[i], out[i]);
while(m--){
int op;
scanf("%d", &op);
if(op == 1){
int Node, x;
scanf("%d %d", &Node, &x);
int l, r;
l = in[Node];
r = out[Node];
Update(1, n, 1, l, r, x);
}
else{
int x;
scanf("%d", &x);
int l, r;
l = in[x];
r = out[x];
printf("%d\n", cal(Query(1, n, 1, l, r)));
}
}
return 0;
}
题意:给定一颗树,在树上进行两种操作。第一种操作为结点X的值增加val,然后结点X的儿子结点的值增加 -val,然后修改结点X的儿子的儿子为 val...直到叶子结点。第二种操作为输出结点X的值。
思路:dfs序处理了原树之后,在树上建线段树。如果规定层数为奇数的层始终是加上val而偶数层始终加上-val,那么只需要直接根据X的层数进行区间修改,在单点查询时也只需要判断当前结点的层数来取正负号。
AC代码:
/*---------------------------------
*File name: B.cpp
*Creation date: 2020-07-05 14:21
*Link:
*-------------------------------*/
#include
#include
题意:给定一个N个顶点的树,有Q次操作,每次操作有两种,第一种是询问当前点到 c 点的边权和。第二种是修改第 i 条边的权值为W。
思路:将边权转化为点权,每条边的权值转化为层数较深的结点的权值,转化后根节点没有权值。按轻重链剖分后的id数组构造线段树,要注意区间查询时的查询区间端点。
AC代码:
/*---------------------------------
*File name: A.cpp
*Creation date: 2020-07-05 11:58
*Link:http://poj.org/problem?id=2763
*-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, q, s;
struct EDGES{
int u, v, w;
}abw[maxn];;
int val[maxn];
struct Edge{
int v, next;
}edge[maxn << 1];
int head[maxn], tot;
struct Tree{
int l, r;
LL w;
}t[maxn << 2];
int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int Time, id[maxn], rk[maxn], top[maxn];
inline void Add_Edge(int u, int v){
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void Dfs1(int u, int p, int d){
pre[u] = p;
dep[u] = d;
sz[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == p) continue;
Dfs1(v, u, d + 1);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void Dfs2(int u, int root){
id[u] = ++Time;
rk[Time] = u;
top[u] = root;
if(!son[u]) return;
Dfs2(son[u], root);
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == pre[u]) continue;
if(v == son[u]) continue;
Dfs2(v, v);
}
}
void Build(int l, int r, int k){
t[k].r = r, t[k].l = l;
if(l == r){
t[k].w = 0;
return;
}
int mid = l + r >> 1;
Build(l, mid, k << 1);
Build(mid + 1, r, k << 1 | 1);
t[k].w = 0;
}
void Update(int l, int r, int k, int x, int c){
if(l == r){
t[k].w = c;
return;
}
int mid = l + r >> 1;
if(x <= mid) Update(l, mid, k << 1, x, c);
else Update(mid + 1, r, k << 1 | 1, x, c);
t[k].w = t[k << 1].w + t[k << 1 | 1].w;
}
LL Query(int l, int r, int k, int L, int R){
if(L <= l && r <= R) return t[k].w;
LL ans = 0;
int mid = l + r >> 1;
if(L <= mid) ans += Query(l, mid, k << 1, L, R);
if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
return ans;
}
LL cal(int u, int v){
LL ans = 0;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
ans += Query(1, n, 1, id[top[u]], id[u]);
u = pre[top[u]];
}
if(id[u] > id[v]) swap(u, v);
if(u == v) return ans;
ans += Query(1, n, 1, id[son[u]], id[v]);
return ans;
}
int main(){
scanf("%d %d %d", &n, &q, &s);
for(int i = 1; i <= n; ++i) head[i] = -1;
for(int i = 1; i < n; ++i){
scanf("%d %d %d", &abw[i].u, &abw[i].v, &abw[i].w);
Add_Edge(abw[i].u, abw[i].v);
Add_Edge(abw[i].v, abw[i].u);
}
Dfs1(1, -1, 1);
Dfs2(1, 1);
Build(1, n, 1);
for(int i = 1; i < n; ++i){
int u = abw[i].u;
int v = abw[i].v;
int w = abw[i].w;
if(dep[u] < dep[v]) swap(u, v);
Update(1, n, 1, id[u], w);
}
while(q--){
int op;
scanf("%d", &op);
if(op){
int i, w;
scanf("%d %d", &i, &w);
int u = abw[i].u;
int v = abw[i].v;
int c = (dep[u] > dep[v] ? u : v);
Update(1, n, 1, id[c], w);
}
else{
int x;
scanf("%d", &x);
LL ans = cal(s, x);
printf("%lld\n", ans);
s = x;
}
}
return 0;
}
题意:给一颗树,问两节点路径距离。
思路:树链剖分+线段树裸题。
AC代码:
/*---------------------------------
*File name: A.cpp
*Creation date: 2020-07-05 11:58
*Link:
*-------------------------------*/
#pragma GCC diagnostic error "-std=c++11"
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, m;
struct EDGES{
int u, v, w;
}abw[maxn];;
int val[maxn];
struct Edge{
int v, next;
}edge[maxn << 1];
int head[maxn], tot;
struct Tree{
int l, r;
LL w;
}t[maxn << 2];
int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int Time, id[maxn], rk[maxn], top[maxn];
inline void Add_Edge(int u, int v){
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void Dfs1(int u, int p, int d){
pre[u] = p;
dep[u] = d;
sz[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == p) continue;
Dfs1(v, u, d + 1);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void Dfs2(int u, int root){
id[u] = ++Time;
rk[Time] = u;
top[u] = root;
if(!son[u]) return;
Dfs2(son[u], root);
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == pre[u]) continue;
if(v == son[u]) continue;
Dfs2(v, v);
}
}
void Build(int l, int r, int k){
t[k].r = r, t[k].l = l;
if(l == r){
t[k].w = val[rk[l]];
return;
}
int mid = l + r >> 1;
Build(l, mid, k << 1);
Build(mid + 1, r, k << 1 | 1);
t[k].w = t[k << 1].w + t[k << 1 | 1].w;
}
LL Query(int l, int r, int k, int L, int R){
if(L <= l && r <= R) return t[k].w;
LL ans = 0;
int mid = l + r >> 1;
if(L <= mid) ans += Query(l, mid, k << 1, L, R);
if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
return ans;
}
LL cal(int u, int v){
LL ans = 0;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
ans += Query(1, n, 1, id[top[u]], id[u]);
u = pre[top[u]];
}
if(id[u] > id[v]) swap(u, v);
if(u == v) return ans;
ans += Query(1, n, 1, id[son[u]], id[v]);
return ans;
}
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++i) head[i] = -1, son[i] = 0, top[i] = 0;
Time = tot = 0;
for(int i = 1; i < n; ++i){
scanf("%d %d %d", &abw[i].u, &abw[i].v, &abw[i].w);
Add_Edge(abw[i].u, abw[i].v);
Add_Edge(abw[i].v, abw[i].u);
}
Dfs1(1, -1, 1);
Dfs2(1, 1);
for(int i = 1; i < n; ++i){
int u = abw[i].u;
int v = abw[i].v;
int w = abw[i].w;
if(dep[u] < dep[v]) swap(u, v);
val[u] = w;
}
Build(1, n, 1);
while(m--){
int x, y;
scanf("%d %d", &x, &y);
LL ans = cal(x, y);
printf("%lld\n", ans);
}
}
return 0;
}
题意:给一颗树,三种操作,第一种为将结点V以及子树中的所有结点都置为1,第二种为将结点V已经V的所有祖先结点置为0,第三种为输出结点V的状态。
思路:树链剖分+线段树裸题。
AC代码:
/*---------------------------------
*File name: F-树链剖分+线段树.cpp
*Creation date: 2020-07-03 10:27
*Link:
*-------------------------------*/
#include
#include
#include
using namespace std;
const int maxn = 5e5 + 5;
int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int top[maxn], id[maxn], rk[maxn], time;
struct Edge{
int v, next;
}edge[maxn << 1];
int head[maxn], tot;
struct Tree{
int l, r, sum;
int f;
}t[maxn << 2];
int n, q;
void Add_Edge(int u, int v){
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void Dfs1(int x, int p, int d){
pre[x] = p;
dep[x] = d;
sz[x] = 1;
for(int i = head[x]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == p) continue;
Dfs1(v, x, d + 1);
sz[x] += sz[v];
if(sz[v] > sz[son[x]]) son[x] = v;
}
return;
}
void Dfs2(int x, int root){
top[x] = root;
id[x] = ++time;
rk[time] = x;
if(!son[x]) return;
Dfs2(son[x], root);
for(int i = head[x]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == pre[x]) continue;
if(v == son[x]) continue;
Dfs2(v, v);
}
}
void Down(int k){
int lc = (k << 1);
int rc = (k << 1 | 1);
t[lc].f = t[rc].f = t[k].f;
t[lc].sum = (t[lc].r - t[lc].l + 1) * t[k].f;
t[rc].sum = (t[rc].r - t[rc].l + 1) * t[k].f;
t[k].f = -1;
}
void Build(int l, int r, int k){
t[k].l = l, t[k].r = r;
t[k].f = -1;
if(l == r){
t[k].sum = 0;
return;
}
int mid = l + r >> 1;
Build(l, mid, k << 1);
Build(mid + 1, r, k << 1 | 1);
t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}
void Update(int l, int r, int k, int L, int R, int c){
if(L <= l && r <= R){
t[k].f = c;
t[k].sum = (r - l + 1) * c;
return;
}
if(t[k].f != -1) Down(k);
int mid = l + r >> 1;
if(L <= mid) Update(l, mid, k << 1, L, R, c);
if(R > mid) Update(mid + 1, r, k << 1 | 1, L, R, c);
t[k].sum = t[k << 1].sum + t[k << 1 | 1].sum;
}
void Change(int u, int v, int c){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
Update(1, n, 1, id[top[u]], id[u], c);
u = pre[top[u]];
}
if(id[u] > id[v]) swap(u, v);
Update(1, n, 1, id[u], id[v], c);
}
int Query(int l, int r, int k, int L, int R){
if(L <= l && r <= R) return t[k].sum;
if(t[k].f != -1) Down(k);
int ans = 0;
int mid = l + r >> 1;
if(L <= mid) ans += Query(l, mid, k << 1, L, R);
if(R > mid) ans += Query(mid + 1, r, k << 1 | 1, L, R);
return ans;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i) head[i] = -1;
for(int i = 1; i < n; ++i){
int u, v;
scanf("%d %d", &u, &v);
Add_Edge(u, v);
Add_Edge(v, u);
}
Dfs1(1, -1, 1);
Dfs2(1, 1);
Build(1, n, 1);
scanf("%d", &q);
while(q--){
int op, x;
scanf("%d %d", &op, &x);
if(op == 1){//灌水
Update(1, n, 1, id[x], id[x] + sz[x] - 1, 1);
}
else if(op == 2){//抽水
Change(1, x, 0);
}
else{//提问
int ans = Query(1, n, 1, id[x], id[x] + sz[x] - 1);
printf("%d\n", ans == sz[x] ? 1 : 0);
}
}
return 0;
}
题意:给一颗树,在树上进行三种操作,第一种是将第 i 条边的权值改为v,第二种是将点 a , b之间的路径的权值全部取相反数,第三种是询问点a, b路径的最大值。
思路:树链剖分+线段树。
AC代码:
/*---------------------------------
*File name: H-树链剖分+线段树.cpp
*Creation date: 2020-07-04 14:59
*Link:
*-------------------------------*/
//#pragma GCC diagnostic error "-std=c++11"
//#include
#include
#include
#include
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
struct Edge{
int v, next;
}edge[maxn << 1];
int head[maxn], tot;
//-----------------
struct Node{
int u, v, w;
};
//-----------------
struct Tree{
int l, r, Max, Min;
bool flag;
}t[maxn << 2];
//-----------------
int dep[maxn], pre[maxn], sz[maxn], son[maxn];
int id[maxn], rk[maxn], top[maxn], Time;
int n, val[maxn];
inline void Add_Edge(int u, int v){
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void Dfs1(int u, int p, int d){
pre[u] = p;
dep[u] = d;
sz[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == p) continue;
Dfs1(v, u, d + 1);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void Dfs2(int u, int root){
id[u] = ++Time;
rk[Time] = u;
top[u] = root;
if(!son[u]) return;
Dfs2(son[u], root);
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(v == pre[u]) continue;
if(v == son[u]) continue;
Dfs2(v, v);
}
}
void Build(int l, int r, int k){
t[k].l = l, t[k].r = r;
if(l == r){
t[k].Max = val[rk[l]];
t[k].Min = val[rk[l]];
t[k].flag = 0;
return;
}
int mid = l + r >> 1;
Build(l, mid, k << 1);
Build(mid + 1, r, k << 1 | 1);
t[k].Max = max(t[k << 1].Max, t[k << 1 | 1].Max);
t[k].Min = min(t[k << 1].Min, t[k << 1 | 1].Min);
t[k].flag = 0;
}
#define lc (k << 1)
#define rc (k << 1 | 1)
inline void swap_Min_Max(int &x, int &y){
int Min = -x;
int Max = -y;
x = Max;
y = Min;
}
inline void Down(int k){
t[rc].flag ^= 1;
t[lc].flag ^= 1;
swap_Min_Max(t[lc].Max, t[lc].Min);
swap_Min_Max(t[rc].Max, t[rc].Min);
t[k].flag = 0;
}
void Update_Point(int l, int r, int k, int x, int c){
if(l == r){
t[k].Max = c;
t[k].Min = c;
return;
}
if(t[k].flag) Down(k);
int mid = l + r >> 1;
if(x <= mid) Update_Point(l, mid, k << 1, x, c);
else Update_Point(mid + 1, r, k << 1 | 1, x, c);
t[k].Max = max(t[lc].Max, t[rc].Max);
t[k].Min = min(t[rc].Min, t[lc].Min);
}
void Update_Ter(int l, int r, int k, int L, int R){
if(L <= l && r <= R){
swap_Min_Max(t[k].Max, t[k].Min);
t[k].flag ^= 1;
return;
}
if(t[k].flag) Down(k);
int mid = l + r >> 1;
if(L <= mid) Update_Ter(l, mid, lc, L, R);
if(R > mid) Update_Ter(mid + 1, r, rc, L, R);
t[k].Max = max(t[lc].Max, t[rc].Max);
t[k].Min = min(t[rc].Min, t[lc].Min);
}
void Change(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
Update_Ter(1, n, 1, id[top[u]], id[u]);
u = pre[top[u]];
}
if(id[u] > id[v]) swap(u, v);
Update_Ter(1, n, 1, id[son[u]], id[v]);
}
int Query(int l, int r, int k, int L, int R){
if(L <= l && r <= R) return t[k].Max;
if(t[k].flag) Down(k);
int mid = l + r >> 1;
int ans = -inf;
if(L <= mid) ans = max(ans, Query(l, mid, lc, L, R));
if(R > mid) ans = max(ans, Query(mid + 1, r, rc, L, R));
return ans;
}
int Cal(int u, int v){
int ans = -inf;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
ans = max(ans, Query(1, n, 1, id[top[u]], id[u]));
u = pre[top[u]];
}
if(id[u] > id[v]) swap(u, v);
ans = max(ans, Query(1, n, 1, id[son[u]], id[v]));
return ans;
}
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++i) head[i] = -1, son[i] = 0;
tot = 0;
Time = 0;
vector vec;
for(int i = 1; i < n; ++i){
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
Add_Edge(u, v);
Add_Edge(v, u);
vec.pb((Node){u, v, w});
}
Dfs1(1, -1, 1);
Dfs2(1, 1);
val[1] = 0;
for(int i = 0; i < n - 1; ++i){
int u, v, w;
u = vec[i].u;
v = vec[i].v;
w = vec[i].w;
if(dep[u] < dep[v]) swap(u, v);
val[u] = w;
}
Build(1, n, 1);
char op[10];
scanf(" %s", op);
while(op[0] != 'D'){
if(op[0] == 'C'){//单点修改
int i, val;
scanf("%d %d", &i, &val);
i--;
int u = vec[i].u, v = vec[i].v;
int c = (dep[u] > dep[v] ? u : v);
Update_Point(1, n, 1, id[c], val);
}
else if(op[0] == 'N'){//区间取相反数
int x, y;
scanf(" %d %d", &x, &y);
Change(x, y);
}
else{//询问
int x, y;
scanf(" %d %d", &x, &y);
int ans = Cal(x, y);
printf("%d\n", ans);
}
scanf(" %s", op);
}
}
return 0;
}
题意:树剖裸题。
思路:线段树。
AC代码:
/*---------------------------------
*File name: G-树链剖分+线段树.cpp
*Creation date: 2020-07-01 11:56
*Link:
*-------------------------------*/
#include
#include