【BZOJ3999】旅游,树链剖分中的有向信息合并

Time:2016.05.26
Author:xiaoyimi
转载注明出处谢谢


传送门
思路:
(调了好久发现是链剖打错了)
线性版本的话可以去看水果姐逛水果店Ⅰ
这个放到了树上,还加了个修改操作
先链剖,再维护最大值及最小值,左->右最大差值,右->左最大差值
区间修改打标记
线段树上的连续区间维护起来很好办
关键就在于怎么维护询问中访问的所有链的信息
之前没有写过类似这样的,向char哥学习了一下,具体就是线段树查询时返回线段树的数据类型的节点,来存储和维护信息。
细节信息个人推荐看代码
注意:
很容易把左右,大小,深浅这些东西搞混了……最后就不知所措= =
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define M 50003
#define inf 0x7ffffff
using namespace std;
int n,q,tot,cnt;
int w[M],fa[M],son[M],siz[M],top[M],dep[M],L[M],pre[M],first[M],lazy[M<<2]; 
struct edge{int v,next;}e[M<<1];
struct seg
{
    int sum_L,sum_R,maxn,minn;
    //sum_L->由深到浅(线段树区间由大到小走),sum_R->由浅到深(线段树区间由小到大) 
}tree[M<<2];
seg Null={-inf,-inf,-inf,inf};
int in()
{
    int t=0;char ch=getchar();bool f=0;
    while (!isdigit(ch)) f=(ch=='-'),ch=getchar();
    while (isdigit(ch)) t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return f?-t:t;
}
void add(int x,int y)
{
    e[++tot]=(edge){y,first[x]};first[x]=tot;
    e[++tot]=(edge){x,first[y]};first[y]=tot;
}
void dfs1(int x)
{
    siz[x]=1;
    for (int i=first[x];i;i=e[i].next)
        if (e[i].v!=fa[x])
            fa[e[i].v]=x,
            dep[e[i].v]=dep[x]+1,
            dfs1(e[i].v),
            siz[x]+=siz[e[i].v],
            son[x]=(siz[son[x]]>siz[e[i].v]?son[x]:e[i].v);
}
void dfs2(int x,int tp)
{
    L[x]=++cnt;
    pre[cnt]=x;
    top[x]=tp;
    if (son[x]) dfs2(son[x],tp);
    else return;
    for (int i=first[x];i;i=e[i].next)
        if (e[i].v!=fa[x]&&e[i].v!=son[x]) dfs2(e[i].v,e[i].v);
}
void pushup(int now)
{
    tree[now].maxn=max(tree[now<<1].maxn,tree[now<<1|1].maxn);
    tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
    tree[now].sum_L=max(tree[now<<1].maxn-tree[now<<1|1].minn,
                    max(tree[now<<1].sum_L,tree[now<<1|1].sum_L));
    tree[now].sum_R=max(tree[now<<1|1].maxn-tree[now<<1].minn,
                    max(tree[now<<1].sum_R,tree[now<<1|1].sum_R));
}
void pushdown(int now)
{
    if (!lazy[now]) return;
    tree[now<<1].maxn+=lazy[now]; 
    tree[now<<1].minn+=lazy[now];
    lazy[now<<1]+=lazy[now]; 
    lazy[now<<1|1]+=lazy[now];
    tree[now<<1|1].maxn+=lazy[now];
    tree[now<<1|1].minn+=lazy[now];
    lazy[now]=0;
}
void build(int now,int begin,int end)
{
    if (begin==end)
    {
        tree[now]=(seg){0,0,w[pre[end]],w[pre[end]]};
        return;
    }
    int mid=begin+end>>1;
    build(now<<1,begin,mid);
    build(now<<1|1,mid+1,end);
    pushup(now);
}
void update(int now,int begin,int end,int l,int r,int val)
{
    if (l<=begin&&end<=r)
    {
        lazy[now]+=val;
        tree[now].maxn+=val;
        tree[now].minn+=val;
        return;
    }
    pushdown(now);
    int mid=begin+end>>1;
    if (mid>=l) update(now<<1,begin,mid,l,r,val);
    if (mid<r) update(now<<1|1,mid+1,end,l,r,val);
    pushup(now);
}
seg get(int now,int begin,int end,int l,int r)
{
    if (l<=begin&&end<=r) return tree[now];
    pushdown(now);
    int mid=begin+end>>1;
    if (mid>=r)
        return get(now<<1,begin,mid,l,r);
    else if (mid<l)
        return get(now<<1|1,mid+1,end,l,r);
    seg ans,
        t1=get(now<<1,begin,mid,l,r),
        t2=get(now<<1|1,mid+1,end,l,r);
    ans.maxn=max(t1.maxn,t2.maxn);
    ans.minn=min(t1.minn,t2.minn);
    ans.sum_L=max(max(t1.sum_L,t2.sum_L),t1.maxn-t2.minn);
    ans.sum_R=max(max(t1.sum_R,t2.sum_R),t2.maxn-t1.minn);
    return ans;
}
main()
{
    n=in();
    for (int i=1;i<=n;i++) w[i]=in();
    for (int i=1;i<n;i++) add(in(),in());
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    q=in();
    int x,y,z;
    while (q--)
    {
        x=in();y=in();z=in();
        seg ans_L=Null,ans_R=Null,t;
        for (int f1=top[x],f2=top[y];f1!=f2;)
        {
            if (dep[f1]<dep[f2])
                t=get(1,1,n,L[f2],L[y]),
                ans_R.sum_R=max(max(ans_R.sum_R,t.sum_R),ans_R.maxn-t.minn),
                ans_R.maxn=max(ans_R.maxn,t.maxn),
                ans_R.minn=min(ans_R.minn,t.minn),
                update(1,1,n,L[f2],L[y],z),
                y=fa[f2],f2=top[y];
            else
                t=get(1,1,n,L[f1],L[x]),
                ans_L.sum_L=max(max(ans_L.sum_L,t.sum_L),t.maxn-ans_L.minn),
                ans_L.maxn=max(ans_L.maxn,t.maxn),
                ans_L.minn=min(ans_L.minn,t.minn),
                update(1,1,n,L[f1],L[x],z),
                x=fa[f1],f1=top[x];
        }
        if (dep[x]<=dep[y])
            t=get(1,1,n,L[x],L[y]),
            ans_R.sum_R=max(max(ans_R.sum_R,t.sum_R),ans_R.maxn-t.minn),
            ans_R.maxn=max(ans_R.maxn,t.maxn),
            ans_R.minn=min(ans_R.minn,t.minn),
            update(1,1,n,L[x],L[y],z);
        else
            t=get(1,1,n,L[y],L[x]),
            ans_L.sum_L=max(max(ans_L.sum_L,t.sum_L),t.maxn-ans_L.minn),
            ans_L.maxn=max(ans_L.maxn,t.maxn),
            ans_L.minn=min(ans_L.minn,t.minn),
            update(1,1,n,L[y],L[x],z);
        printf("%d\n",max(ans_R.maxn-ans_L.minn,max(ans_L.sum_L,ans_R.sum_R)));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值