题目描述
给定一棵 n 个节点的树,边带权,编号 0∼n−1,需要支持五种操作:
C i w 将输入的第 i 条边权值改为 w
N u v 将 u,v 节点之间的边权都变为相反数
SUM u v 询问 u,v 节点之间边权和
MAX u v 询问 u,v 节点之间边权最大值
MIN u v 询问 u,v 节点之间边权最小值
保证任意时刻所有边的权值都在 [-1000,1000] 内。
分析
树剖裸题。
需要特殊处理的好像只有C操作。
当时实际上,边权转点权的时候细节需要注意!(因为太菜了 在这里被坑了)
树中:边权转点权
如果只有一个点,要特殊处理
假设:1->2 边权为2。
现在将边权放在点2(w[2]=2)上。
当处理[1~2]的时候,没问题,w[2]可以正常使用。
但是在处理[2~2](2一个点的时候)时,改区间的边权应该是0(没有边)。
普遍的,边权转点权之后,处理l~r区间的时候应该是(l,r]。
所以,在树剖统计的时候需要判断
丑陋的代码
#include <bits/stdc++.h>
using namespace std;
//-----pre_def----
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define fir(i, a, b) for (int i = (a); i <= (b); i++)
#define rif(i, a, b) for (int i = (a); i >= (b); i--)
#define endl '\n'
#define init_h memset(h, -1, sizeof h), idx = 0;
#define lowbit(x) x &(-x)
//---------------
const int N = 2e5 + 10, M = N * 2;
int w[M], h[N], e[M], ne[M], idx;
int nd[N], dep[N], fa[N], son[N], top[N], sz[N], id[N], d[N];
int n, root = 1, cnt;
PII iid[N];
struct node
{
int l, r, sum, maxx, minn;
int lz;
} tr[N << 2];
//线段树部分
void add(int a, int b, int v)
{
e[idx] = b, ne[idx] = h[a], w[idx] = v, h[a] = idx++;
}
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
tr[u].maxx = max(tr[u << 1].maxx, tr[u << 1 | 1].maxx);
tr[u].minn = min(tr[u << 1].minn, tr[u << 1 | 1].minn);
}
void pushdown(int u)
{
if (tr[u].lz)
{
tr[u << 1].sum *= -1;
tr[u << 1].maxx *= -1;
tr[u << 1].minn *= -1;
swap(tr[u << 1].maxx, tr[u << 1].minn);
tr[u << 1 | 1].sum *= -1;
tr[u << 1 | 1].maxx *= -1;
tr[u << 1 | 1].minn *= -1;
swap(tr[u << 1 | 1].maxx, tr[u << 1 | 1].minn);
tr[u << 1].lz ^= 1;
tr[u << 1 | 1].lz ^= 1;
}
tr[u].lz = 0;
}
void build(int u, int l, int r)
{
tr[u] = {l, r, 0, 0, 0, 0};
if (l == r)
{
tr[u] = {l, r, nd[l], nd[l], nd[l], 0};
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify1(int u, int l, int r, int k) //已经不会写单点修改了(ㄒxㄒ)
{
if (l <= tr[u].l && tr[u].r <= r)
{
tr[u].sum = k;
tr[u].maxx = k;
tr[u].minn = k;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
modify1(u << 1, l, r, k);
if (mid < r)
modify1(u << 1 | 1, l, r, k);
pushup(u);
}
void modify2(int u, int l, int r) //区间乘-1
{
if (l <= tr[u].l && tr[u].r <= r)
{
tr[u].sum *= -1;
int tmp1 = tr[u].maxx;
int tmp2 = tr[u].minn;
tr[u].maxx = -tmp2;
tr[u].minn = -tmp1;
tr[u].lz ^= 1;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
modify2(u << 1, l, r);
if (mid < r)
modify2(u << 1 | 1, l, r);
pushup(u);
}
int query_s(int u, int l, int r) //查询区间和
{
if (l <= tr[u].l && tr[u].r <= r)
{
return tr[u].sum;
}
pushdown(u);
int res = 0;
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
res += query_s(u << 1, l, r);
if (mid < r)
res += query_s(u << 1 | 1, l, r);
pushup(u);
return res;
}
int query_minn(int u, int l, int r) //查询区间最小值
{
if (l <= tr[u].l && tr[u].r <= r)
{
return tr[u].minn;
}
pushdown(u);
int res = INF;
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
res = min(res, query_minn(u << 1, l, r));
if (mid < r)
res = min(res, query_minn(u << 1 | 1, l, r));
pushup(u);
return res;
}
int query_maxx(int u, int l, int r) //查询区间最大值
{
if (l <= tr[u].l && tr[u].r <= r)
{
return tr[u].maxx;
}
pushdown(u);
int res = -INF;
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
res = max(res, query_maxx(u << 1, l, r));
if (mid < r)
res = max(res, query_maxx(u << 1 | 1, l, r));
pushup(u);
return res;
}
//树剖部分
void dfs1(int u, int father, int depth)
{
dep[u] = depth, fa[u] = father, sz[u] = 1;
for (int i = h[u]; ~i; i = ne[i])
{
int t = e[i];
if (t == father)
continue;
dfs1(t, u, depth + 1);
d[t] = w[i];
sz[u] += sz[t];
if (sz[son[u]] < sz[t])
son[u] = t;
}
}
void dfs2(int u, int t)
{
id[u] = ++cnt;
nd[cnt] = d[u];
top[u] = t; //u点所在重链的顶端是t
if (!son[u])
return;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa[u] || j == son[u])
continue;
dfs2(j, j);
}
}
void modify_path(int u, int v)
{
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
swap(u, v);
modify2(1, id[top[u]], id[u]);
u = fa[top[u]];
}
if (dep[u] < dep[v])
swap(u, v);
if (u != v) //要判断!!
modify2(1, id[v] + 1, id[u]);
}
int query_sum(int u, int v)
{
int res = 0;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
swap(u, v);
res += query_s(1, id[top[u]], id[u]);
u = fa[top[u]];
}
if (dep[u] < dep[v])
swap(u, v);
if (u != v) //要判断!!
res += query_s(1, id[v] + 1, id[u]);
return res;
}
int query_max(int u, int v)
{
int res = -INF;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
swap(u, v);
res = max(res, query_maxx(1, id[top[u]], id[u]));
u = fa[top[u]];
}
if (dep[u] < dep[v])
swap(u, v);
if (u != v) //要判断!!
res = max(res, query_maxx(1, id[v] + 1, id[u]));
return res;
}
int query_min(int u, int v)
{
int res = INF;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
swap(u, v);
res = min(res, query_minn(1, id[top[u]], id[u]));
u = fa[top[u]];
}
if (dep[u] < dep[v])
swap(u, v);
if (u != v) //要判断!!
res = min(res, query_minn(1, id[v] + 1, id[u]));
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
int StartTime = clock();
#endif
scanf("%d", &n);
init_h;
fir(i, 1, n - 1)
{
int a, b, v;
scanf("%d%d%d", &a, &b, &v);
add(a + 1, b + 1, v);
add(b + 1, a + 1, v);
iid[i] = {a + 1, b + 1};
}
dfs1(1, -1, 1);
dfs2(1, 1);
build(1, 1, n);
int m;
scanf("%d", &m);
while (m--)
{
char op[5];
int u, v;
scanf("%s%d%d", op, &u, &v);
if (!strcmp(op, "SUM"))
{
printf("%d\n", query_sum(u + 1, v + 1));
}
else if (!strcmp(op, "MAX"))
{
printf("%d\n", query_max(u + 1, v + 1));
}
else if (!strcmp(op, "MIN"))
{
printf("%d\n", query_min(u + 1, v + 1));
}
else if (!strcmp(op, "C"))
{
int tmp;
if (dep[iid[u].first] > dep[iid[u].second])
{
tmp = id[iid[u].first];
}
else
{
tmp = id[iid[u].second];
}
modify1(1, tmp, tmp, v);
}
else
{
modify_path(u + 1, v + 1);
}
}
#ifndef ONLINE_JUDGE
printf("Run_Time = %d ms\n", clock() - StartTime);
#endif
return 0;
}
大佬们的线段树写得好漂亮,下回去学学。。