洛谷P1505 [国家集训队]旅游 (树链剖分+难写的线段树+边权转点权)

本文详细阐述了一种解决树状结构边权操作问题的算法,涉及Ciw操作的特殊处理,以及如何通过树剖和线段树技术实现边权转点权、SUM、MAX和MIN查询。关键在于理解边权到节点权的转换细节和区间处理的边界条件。
摘要由CSDN通过智能技术生成

传送门

题目描述

给定一棵 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;
}

大佬们的线段树写得好漂亮,下回去学学。。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值