洛谷P1505 [国家集训队]旅游 (树链剖分)

洛谷P1505 [国家集训队]旅游

题目描述
R a y Ray Ray 乐忠于旅游,这次他来到了 T T T 城。 T T T 城是一个水上城市,一共有 N N N 个景点,有些景点之间会用一座桥连接。为了方便游客到达每个景点但又为了节约成本,$T $城的任意两个景点之间有且只有一条路径。换句话说, T T T 城中只有 N − 1 N − 1 N1 座桥。

R a y Ray Ray 发现,有些桥上可以看到美丽的景色,让人心情愉悦,但有些桥狭窄泥泞,令人烦躁。于是,他给每座桥定义一个愉悦度 w w w,也就是说, R a y Ray Ray 经过这座桥会增加 w w w 的愉悦度,这或许是正的也可能是负的。有时, R a y Ray Ray 看待同一座桥的心情也会发生改变。

现在, R a y Ray Ray 想让你帮他计算从 u u u 景点到 v v v 景点能获得的总愉悦度。有时,他还想知道某段路上最美丽的桥所提供的最大愉悦度,或是某段路上最糟糕的一座桥提供的最低愉悦度。

输入输出格式
输入格式:
输入的第一行包含一个整数 N N N,表示 T T T 城中的景点个数。景点编号为 0... N − 1 0...N − 1 0...N1

接下来 N − 1 N − 1 N1 行,每行三个整数 u 、 v u、v uv w w w,表示有一条 u u u v v v,使 R a y Ray Ray 愉悦度增加 w w w 的桥。桥的编号为 1... N − 1 1...N − 1 1...N1 ∣ w ∣ < = 1000 |w| <= 1000 w<=1000。 输入的第 N + 1 N + 1 N+1 行包含一个整数 M M M,表示 R a y Ray Ray 的操作数目。

接下来有 M M M 行,每行描述了一个操作,操作有如下五种形式:

C i w C i w Ciw,表示 R a y Ray Ray 对于经过第i 座桥的愉悦度变成了 w w w

N u v N u v Nuv,表示 R a y Ray Ray 对于经过景点 u u u v v v 的路径上的每一座桥的愉悦度都变成原来的相反数。

S U M u v SUM u v SUMuv,表示询问从景点 u u u v v v 所获得的总愉悦度。

M A X u v MAX u v MAXuv,表示询问从景点 u u u v v v 的路径上的所有桥中某一座桥所提供的最大愉悦度。

M I N u v MIN u v MINuv,表示询问从景点 u u u v v v 的路径上的所有桥中某一座桥所提供的最小愉悦度。

测试数据保证,任意时刻, R a y Ray Ray 对于经过每一座桥的愉悦度的绝对值小于等于 1000 1000 1000

输出格式:
对于每一个询问(操作 S 、 M A X S、MAX SMAX M I N MIN MIN),输出答案。

输入样例#1:

3
0 1 1
1 2 2
8
SUM 0 2
MAX 0 2
N 0 1
SUM 0 2
MIN 0 2
C 1 3
SUM 0 2
MAX 0 2

输出样例#1:

3
2
1
-1
5
3

emmm
这道题是一道很经典的树剖模板题,他基本上维护了日常线段树维护的所有东西。什么区间最大值,最小值,单点修改,区间乘法 。光线段树部分就码了100多行。

一般的树链剖分维护的都是点权,但这个题是维护的边权,怎么办呢。仔细观察 我们可以发现每一个节点有且只有一个父亲 (废话) ,那么说,每个节点连向父亲的边都是唯一的。所以我们可以把边权转移到这条边深度更深的点上。这样除了根节点每一个节点都拥有了一个点权。我们在修改一条路径的时候只需要正常修改这条路径上除了lca的所有点就可以了
比如这样

ans += querys(1, num[x] + 1, num[y]);

在 x 和 y 的top相同时 , 设x为深度较小的那个点,则x就为lca。因为我们不改lca,所以我们就修改num[ x ] + 1 到 num[ y ]。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int maxn = 120000;
#define ll long long
#define ls now<<1
#define rs now<<1|1
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}
struct node
{
    ll f, t, v;
}e[maxn << 1];
struct tree
{
    ll l, r, sum, maxx, minn, add, mul , num;
}tre[maxn << 2];
ll n, m, tot, cnt;
ll head[maxn], nxt[maxn << 1], used[maxn], dep[maxn], fa[maxn], sz[maxn], w1[maxn];
ll w2[maxn] , son[maxn] , top[maxn] , num[maxn];
inline void buildnode(ll a, ll b , ll c)
{
    tot++;
    e[tot].f = a;
    e[tot].t = b;
    e[tot].v = c;
    nxt[tot] = head[a];
    head[a] = tot;
}
inline void pushup(ll now)
{
    tre[now].sum = tre[ls].sum + tre[rs].sum;
    tre[now].maxx = max(tre[ls].maxx, tre[rs].maxx);
    tre[now].minn = min(tre[ls].minn, tre[rs].minn);
}
inline void build(ll now, ll l, ll r)
{
    tre[now].l = l;
    tre[now].r = r;
    tre[now].num = r - l + 1;
    if (l == r)
    {
        tre[now].sum = w2[l];
        tre[now].maxx = w2[l];
        tre[now].minn = w2[l];
        return;
    }
    ll mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1 , r);
    pushup(now);
}
inline void pushdown(ll now)
{
    if (tre[now].mul)
    {
        tre[ls].sum *= -1;
        tre[rs].sum *= -1;
        ll t = tre[ls].maxx;
        tre[ls].maxx = -tre[ls].minn;
        tre[ls].minn = -t;
        t = tre[rs].maxx;
        tre[rs].maxx = -tre[rs].minn;
        tre[rs].minn = -t;
    }
    tre[ls].mul = (tre[ls].mul + tre[now].mul) % 2;
    tre[rs].mul = (tre[rs].mul + tre[now].mul) % 2;
    tre[now].mul = 0;
}
inline void update1(ll now, ll x ,  ll p)
{
    if (tre[now].l == tre[now].r)
    {
        tre[now].sum = p;
        tre[now].maxx = p;
        tre[now].minn = p;
        return;
    }
    pushdown(now);
    ll mid = (tre[now].l + tre[now].r) >> 1;
    if (x <= mid) update1(ls, x, p);
    if (x > mid) update1(rs, x, p);
    pushup(now);
}
inline void update2(ll now, ll l, ll r)
{
    if (l <= tre[now].l && tre[now].r <= r)
    {
        tre[now].sum = -tre[now].sum;
        tre[now].mul = (tre[now].mul + 1) % 2;
        ll t = tre[now].maxx;
        tre[now].maxx = -tre[now].minn;
        tre[now].minn = -t;
        return;
    }
    pushdown(now);
    ll mid = (tre[now].l + tre[now].r) >> 1;
    if (l <= mid) update2(ls, l, r);
    if (r > mid) update2(rs, l, r);
    pushup(now);
}
inline void dfs1(ll x, ll fat)
{
    dep[x] = dep[fat] + 1;
    fa[x] = fat;
    sz[x] = 1;
    int k = -1;
    for (int i = head[x]; i; i = nxt[i])
    {
        int u = e[i].t;
        if (u == fa[x]) continue;
        w1[u] = e[i].v;
        dfs1(u, x);
        sz[x] += sz[u];
        if (sz[u] > k) k = sz[u], son[x] = u;
    }
}
inline void dfs2(ll x , ll topx)
{
    num[x] = ++cnt;
    w2[cnt] = w1[x];
    top[x] = topx;
    if (!son[x]) return;
    dfs2(son[x], topx);
    for (int i = head[x]; i; i = nxt[i])
    {
        int u = e[i].t;
        if (u == fa[x] || u == son[x]) continue;
        dfs2(u, u);
    }
}
inline int querys(ll now, ll l, ll r)
{
    if (l <= tre[now].l && tre[now].r <= r)
        return tre[now].sum;
    pushdown(now);
    ll mid = (tre[now].l + tre[now].r) >> 1, ans = 0;
    if (l <= mid) ans += querys(ls, l, r);
    if (r > mid) ans += querys(rs, l, r);
    return ans;
}
inline ll query_max(ll now, ll l , ll r)
{
    if (l <= tre[now].l &&tre[now].r <= r)
        return tre[now].maxx;
    pushdown(now);
    ll mid = (tre[now].l + tre[now].r) >> 1, ans = -1e9;
    if (l <= mid) ans = max(ans, query_max(ls, l, r));
    if (r > mid) ans = max(ans, query_max(rs, l, r));
    return ans;
}
inline ll query_min(ll now, ll l , ll r)
{
    if (l <= tre[now].l &&tre[now].r <= r)
        return tre[now].minn;
    pushdown(now);
    ll mid = (tre[now].l + tre[now].r) >> 1, ans = 1e9;
    if (l <= mid) ans = min(ans, query_min(ls, l, r));
    if (r > mid) ans = min(ans, query_min(rs, l, r));
    return ans;
}
inline ll qline(ll x, ll y)
{
    ll ans = 0;
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x , y);
            ans += querys(1, num[top[x]], num[x]);
        x = fa[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    ans += querys(1, num[x] + 1, num[y]);
    return ans;
}
inline void cline(ll x, ll y)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x , y);
            update2(1, num[top[x]], num[x]);
        x = fa[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    update2(1, num[x] + 1, num[y]);
}
inline ll qpmax(ll x, ll y)
{
    ll ans = -1e9;
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x , y);
            ans = max(ans, query_max(1, num[top[x]], num[x]));
        x = fa[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    ans = max(ans, query_max(1, num[x] + 1, num[y]));
    return ans;
}
inline ll qpmin(ll x, ll y)
{
    ll ans = 1e9;
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x , y);
            ans = min(ans, query_min(1, num[top[x]], num[x]));
        x = fa[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    ans = min(ans, query_min(1, num[x] + 1, num[y]));
    return ans;
}
string s1 = "C", s2 = "N", s3 = "SUM", s4 = "MAX", s5 = "MIN";
int main()
{
    //freopen("1.in","r",stdin);
    n = read();
    for (int i = 1; i <= n - 1; i++)
    {
        int a, b, c;
        a = read(); b = read(); c = read();
        buildnode(a + 1, b + 1, c);
        buildnode(b + 1, a + 1, c);
    }
    dfs1(1 , 0);
    dfs2(1 , 1);
    build(1 , 1 , n);
    m = read();
    while (m--)
    {
        string s;
        int a, b, k;
        cin >> s >> a >> b;
        a += 1 , b += 1;
        if (s == s1)
        {
            a -= 1 , b -= 1;
            if (dep[e[a * 2 - 1].f] > dep[e[a * 2 - 1].t])
                k = e[a * 2 - 1].f;
            else k = e[a * 2 - 1].t;
            update1(1, num[k], b);
        }
        if (s == s2) cline(a, b);
        if (s == s3) printf("%lld\n", qline(a, b));
        if (s == s4) printf("%lld\n", qpmax(a, b));
        if (s == s5) printf("%lld\n", qpmin(a, b));
    }
    return 0;
}

总的来说这个题代码量还是大。所以说做这个题的时候一定要仔细。。
End;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值