BZOJ 2157: 旅游 (树链剖分+边权转点权)

题目链接

Description

Ray 乐忠于旅游,这次他来到了T 城。T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接。为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路径。换句话说, T 城中只有N − 1 座桥。Ray 发现,有些桥上可以看到美丽的景色,让人心情愉悦,但有些桥狭窄泥泞,令人烦躁。于是,他给每座桥定义一个愉悦度w,也就是说,Ray 经过这座桥会增加w 的愉悦度,这或许是正的也可能是负的。有时,Ray 看待同一座桥的心情也会发生改变。现在,Ray 想让你帮他计算从u 景点到v 景点能获得的总愉悦度。有时,他还想知道某段路上最美丽的桥所提供的最大愉悦度,或是某段路上最糟糕的一座桥提供的最低愉悦度。

Input

输入的第一行包含一个整数N,表示T 城中的景点个数。景点编号为 0…N − 1。接下来N − 1 行,每行三个整数u、v 和w,表示有一条u 到v,使 Ray 愉悦度增加w 的桥。桥的编号为1…N − 1。|w| <= 1000。输入的第N + 1 行包含一个整数M,表示Ray 的操作数目。接下来有M 行,每行描述了一个操作,操作有如下五种形式: C i w,表示Ray 对于经过第i 座桥的愉悦度变成了w。 N u v,表示Ray 对于经过景点u 到v 的路径上的每一座桥的愉悦度都变成原来的相反数。 SUM u v,表示询问从景点u 到v 所获得的总愉悦度。 MAX u v,表示询问从景点u 到v 的路径上的所有桥中某一座桥所提供的最大愉悦度。 MIN u v,表示询问从景点u 到v 的路径上的所有桥中某一座桥所提供的最小愉悦度。测试数据保证,任意时刻,Ray 对于经过每一座桥的愉悦度的绝对值小于等于1000。

Output

对于每一个询问(操作S、MAX 和MIN),输出答案。

Sample Input

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

Sample Output

3
2
1
-1
5
3

HINT

共有10 个数据,对于第i (1 <= i <= 10) 个数据, N = M = i * 2000。

题目是树面剖分裸题,不过代码量比较毒瘤,还需要进行边权向点权的转化
可以把边权全部向下和点联系,根节点的权为0,就完成了边权到点权的转化
然后在进行树面剖分区间计算的时候不算两点的最近公共祖先,因为这一点的权值不属于要计算的两点间的边
具体的边权到点权的转化可以在第一次dfs中进行,存储边的时候将边权也存起来,dfs遍历边时将边权存在深度更深的点上(数组存储关系)
而对于边权的修改也需要一个对应关系 边–>存改边的边权的点,也在第一次dfs中处理(数组存储关系)

代码

#include <queue>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define  ll long long
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int a[maxn],q[maxn],siz[maxn],son[maxn],fa[maxn],deep[maxn],top[maxn],id[maxn],pre[maxn],cnt;
struct nod//存储边点
{
    int id, v;
    int xh;
};
vector<nod> g[maxn];
struct node//线段树节点
{
    int l,r,lazy;//lazy标记是否取反
    int max_,min_;
    int num;
};
node w[maxn<<2];
void pushup(int e)//向上更新
{
    w[e].num=w[e<<1].num+w[e<<1|1].num;
    w[e].max_=max(w[e<<1].max_,w[e<<1|1].max_);
    w[e].min_=min(w[e<<1].min_,w[e<<1|1].min_);
}
void pushnow(int e)
{
    swap(w[e].max_,w[e].min_);
    w[e].lazy^=1;
    w[e].max_=-w[e].max_;
    w[e].min_=-w[e].min_;
    w[e].num=-w[e].num;
}
void pushdown(int e)//向下更新
{
    if(w[e].lazy)
    {
        pushnow(e<<1);
        pushnow(e<<1|1);
        w[e].lazy=0;
    }
}
void dfs1(int e,int f)
{
    siz[e]=1;
    fa[e]=f;
    son[e]=0;
    for(int i=0; i<g[e].size(); ++i)
    {
        int x=g[e][i].id;
        if(x==f)
            continue;
        deep[x]=deep[e]+1;
        a[x]=g[e][i].v;
        q[g[e][i].xh]=x;//将桥的序号与点联系
        dfs1(x,e);
        siz[e]+=siz[x];
        if(siz[son[e]]<siz[x])
            son[e]=x;
    }
}
void dfs2(int e,int tp)
{
    id[e]=++cnt;
    top[e]=tp;
    pre[id[e]]=e;
    if(son[e])
        dfs2(son[e],tp);
    for(int i=0; i<g[e].size(); ++i)
    {
        int x=g[e][i].id;
        if(x!=fa[e]&&x!=son[e])
            dfs2(x,x);
    }
}
void build(int e,int l,int r)
{
    w[e].l=l,w[e].r=r,w[e].lazy=0;
    if(l==r)
    {
        w[e].num=w[e].max_=w[e].min_=a[pre[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(e<<1,l,mid);
    build(e<<1|1,mid+1,r);
    pushup(e);
}
void become(int e,int a,int b)//单点修改
{
    if(w[e].l==w[e].r)
    {
        w[e].num=w[e].max_=w[e].min_=b;
        return;
    }
    pushdown(e);
    int mid=(w[e].l+w[e].r)>>1;
    if(a<=mid)
        become(e<<1,a,b);
    else
        become(e<<1|1,a,b);
    pushup(e);
}
void update_inverse(int e,int a,int b)
{
    if(w[e].l>=a&&w[e].r<=b)
    {
        pushnow(e);
        return;
    }
    pushdown(e);
    int mid=(w[e].l+w[e].r)>>1;
    if(a<=mid)
        update_inverse(e<<1,a,b);
    if(b>mid)
        update_inverse(e<<1|1,a,b);
    pushup(e);
}
void u_inverse(int a,int b)
{
    while(top[a]!=top[b])
    {
        if(deep[top[a]]<deep[top[b]])
            swap(a,b);
        update_inverse(1,id[top[a]],id[a]);
        a=fa[top[a]];
    }
    if(deep[a]>deep[b])
        swap(a,b);
    if(a!=b)
    update_inverse(1,id[a]+1,id[b]);
}
int query_sum(int e,int a,int b)
{
    if(w[e].l>=a&&w[e].r<=b)
        return w[e].num;
    pushdown(e);
    int mid=(w[e].l+w[e].r)>>1,ans=0;
    if(a<=mid)
        ans+=query_sum(e<<1,a,b);
    if(b>mid)
        ans+=query_sum(e<<1|1,a,b);
    pushup(e);
    return ans;
}
int q_sum(int a,int b)
{
    int ans=0;
    while(top[a]!=top[b])
    {
        if(deep[top[a]]<deep[top[b]])
            swap(a,b);
        ans+=query_sum(1,id[top[a]],id[a]);
        a=fa[top[a]];
    }
    if(deep[a]>deep[b])
        swap(a,b);
    if(a!=b)
    ans+=query_sum(1,id[a]+1,id[b]);
    return ans;
}
int query_max(int e,int a,int b)
{
    if(w[e].l>=a&&w[e].r<=b)
    {
        return w[e].max_;
    }
    pushdown(e);
    int mid=(w[e].l+w[e].r)>>1,ans=-inf;
    if(a<=mid)
        ans=max(ans,query_max(e<<1,a,b));
    if(b>mid)
        ans=max(ans,query_max(e<<1|1,a,b));
    pushup(e);
    return ans;
}
int q_max(int a,int b)
{
    int ans=-inf;
    while(top[a]!=top[b])
    {
        if(deep[top[a]]<deep[top[b]])
            swap(a,b);
        ans=max(ans,query_max(1,id[top[a]],id[a]));
        a=fa[top[a]];
    }
    if(deep[a]>deep[b])
        swap(a,b);
    if(a!=b)
    ans=max(ans,query_max(1,id[a]+1,id[b]));
    return ans;
}
int query_min(int e,int a,int b)
{
    if(w[e].l>=a&&w[e].r<=b)
    {
        return w[e].min_;
    }
    pushdown(e);
    int mid=(w[e].l+w[e].r)>>1,ans=inf;
    if(a<=mid)
        ans=min(ans,query_min(e<<1,a,b));
    if(b>mid)
        ans=min(ans,query_min(e<<1|1,a,b));
    pushup(e);
    return ans;
}
int q_min(int a,int b)
{
    int ans=inf;
    while(top[a]!=top[b])
    {
        if(deep[top[a]]<deep[top[b]])
            swap(a,b);
        ans=min(ans,query_min(1,id[top[a]],id[a]));
        a=fa[top[a]];
    }
    if(deep[a]>deep[b])
        swap(a,b);
    if(a!=b)
    ans=min(ans,query_min(1,id[a]+1,id[b]));
    return ans;
}
int main()
{
    //freopen("text.txt","r",stdin);
    int n,m;
    scanf("%d",&n);
    a[0]=0,q[0]=0,cnt=0,deep[1]=1;//没有用的初始化
    for(int i=1; i<n; ++i)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        ++x,++y;
        nod ne1,ne2;
        ne1.id=x,ne2.id=y;
        ne1.v=z,ne2.v=z;
        ne1.xh=i,ne2.xh=i;
        g[x].push_back(ne2);
        g[y].push_back(ne1);
    }
    dfs1(1,1);
    dfs2(1,1);
    build(1,1,n);
    scanf("%d",&m);
    char x[9];
    while(m--)
    {
        int a,b;
        scanf("%s",x);
        scanf("%d %d",&a,&b);
        if(x[0]=='C')//换点值
            become(1,id[q[a]],b);
        else if(x[0]=='N')//区间元素取负数
            u_inverse(a+1,b+1);
        else if(x[0]=='S')//区间和
            printf("%d\n",q_sum(a+1,b+1));
        else if(x[1]=='A')//区间最大值
            printf("%d\n",q_max(a+1,b+1));
        else//区间最小值
            printf("%d\n",q_min(a+1,b+1));
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值