洛谷题单指南-图论之树-P3313 [SDOI2014] 旅行
原题链接:https://www.luogu.com.cn/problem/P3313
题意解读:一棵树的节点有两个属性:评级w和信仰c,树上支持4种操作:1.单点修改评级 2.单点修改信仰 3.查询路径上和起点信仰相同节点的评级之和 4.查询路径上和起点信仰相同节点的最大评级。
解题思路:显然也是一道树链剖分+线段树的问题,关键在于如何构建线段树。
由于每次查询必须限制同一种信仰的节点,可以考虑按照不同的信仰构建多棵线段树,信仰i对应的线段树根节点设为root[i]。
如果完整构建多棵线段树,空间显然撑不住,又可以借助动态开点大法,关于动态开点可以翻看之前的题解:
https://www.cnblogs.com/jcwy/p/18673073
https://www.cnblogs.com/jcwy/p/18640370
剩下的内容就是常规的树链剖分、线段树相关操作。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int w[N], c[N];
vector<int> g[N];
int son[N], siz[N], fa[N], depth[N], top[N], dfn[N], rk[N], cnt;
struct Node
{
int L, R;
int sum; //区间w值之和
int maxw; //区间最大w值
} tr[N * 80];
int root[N], idx; //root[i]表示c值为i的权值线段树根节点
int n, m;
void dfs1(int u, int p, int d)
{
depth[u] = d;
fa[u] = p;
siz[u] = 1;
for(auto v : g[u])
{
if(v == p) continue;
dfs1(v, u, d + 1);
siz[u] += siz[v];
if(siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int t)
{
top[u] = t;
dfn[u] = ++cnt;
rk[cnt] = u;
if(son[u]) dfs2(son[u], t);
for(auto v : g[u])
{
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
void pushup(Node &root, Node &left, Node &right)
{
root.sum = left.sum + right.sum;
root.maxw = max(left.maxw, right.maxw);
}
void pushup(int u)
{
pushup(tr[u], tr[tr[u].L], tr[tr[u].R]);
}
//将根为u的线段树中pos位置的w权值设置为val
int update(int u, int l, int r, int pos, int val)
{
if(!u) u = ++idx;
if(l == r)
{
tr[u].sum = val;
tr[u].maxw = val;
return u;
}
int mid = l + r >> 1;
if(pos <= mid) tr[u].L = update(tr[u].L, l, mid, pos, val);
else tr[u].R = update(tr[u].R, mid + 1, r, pos, val);
pushup(u);
return u;
}
//查询根为u的线段树中L~R区间的sum和maxw
Node query(int u, int l, int r, int L, int R)
{
if(l >= L && r <= R) return tr[u];
else if(l > R || r < L) return {0, 0, 0 ,0};
else
{
int mid = l + r >> 1;
Node res = {0, 0, 0, 0};
Node left = query(tr[u].L, l, mid, L, R);
Node right = query(tr[u].R, mid + 1, r, L, R);
pushup(res, left, right);
return res;
}
}
//查询u到v的路径上信仰为c的sum和maxw
Node queryPath(int c, int u, int v)
{
Node res = {0, 0, 0, 0};
while(top[u] != top[v])
{
if(depth[top[u]] < depth[top[v]]) swap(u, v);
Node tmp = query(root[c], 1, n, dfn[top[u]], dfn[u]);
pushup(res, tmp, res);
u = fa[top[u]];
}
if(depth[u] > depth[v]) swap(u, v);
Node tmp = query(root[c], 1, n, dfn[u], dfn[v]);
pushup(res, tmp, res);
return res;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> w[i] >> c[i];
for(int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0, 1);
dfs2(1, 1);
for(int i = 1; i <= n; i++) root[c[i]] = update(root[c[i]], 1, n, dfn[i], w[i]);
while (m--)
{
string op;
int x, y;
cin >> op >> x >> y;
if(op == "CC")
{
//将root[c[x]]中dfn[x]位置的权值设置为0
root[c[x]] = update(root[c[x]], 1, n, dfn[x], 0);
//将root[y]中dfn[x]位置的权值设置为w[x]
root[y] = update(root[y], 1, n, dfn[x], w[x]);
c[x] = y;
}
else if(op == "CW")
{
//将root[c[x]]中dfn[x]位置的权值设置为y
root[c[x]] = update(root[c[x]], 1, n, dfn[x], y);
w[x] = y;
}
else if(op == "QS")
{
Node res = queryPath(c[x], x, y);
cout << res.sum << endl;
}
else if(op == "QM")
{
Node res = queryPath(c[x], x, y);
cout << res.maxw << endl;
}
}
}
浙公网安备 33010602011771号