TJOI2015 旅游

题目链接:戳我

树上有向信息合并。

用树链剖分把其转化为序列上的问题——求区间中最大数-最小数的差,限制为最小数必须在最大数前面选取。

有两种选择方法,一种是先选择全局最大数,在它前面区间中选择最小数。一种是选择全局最小数,然后在它后面的区间种选择最大的。(证明只有这两种情况——加入我们选择了一个全局次大,只有当最小数小于它前面区间最小数才有可能最优,最佳情况下会选到全局最小。但是由于它的全局次大在这个选择的最小数的后面,而这又一定不比选择全局最小+其后面的最大更优)

对于一条链(u,v),我们需要记录四个量——最小值,最大值,u->v的最大差,v->u的最大差(记录后面这两个是因为我们不知道出发点和终点哪一个的dfn更大)

合并后面两个量的时候注意有两种情况,一种是直接取左右儿子中的最大值(也就是左右两个区间内进行选择的最大值),一种是跨区间,需要lson.max-rson.min或者rson.max-lson.min。

然后最后更新答案的时候不要忘了有两种选择方法哦,要分类讨论取最优qwq~

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define check
#define MAXN 100010
#define INF 1e18
using namespace std;

int n,m,tt,cnt;
int head[MAXN],dfn[MAXN],siz[MAXN],dep[MAXN],fa[MAXN],top[MAXN],son[MAXN];
long long p_max[MAXN],p_min[MAXN],q_min[MAXN],q_max[MAXN],w[MAXN],wt[MAXN];

struct Edge{int nxt,to;}edge[MAXN<<1];
struct Node{long long maxx,minn,d,_d,tag;}t[MAXN<<2];

inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void add(int from,int to){edge[++tt].nxt=head[from],edge[tt].to=to,head[from]=tt;}

inline void push_up(int x)
{
    t[x].maxx=max(t[ls(x)].maxx,t[rs(x)].maxx);
    t[x].minn=min(t[ls(x)].minn,t[rs(x)].minn);
    t[x].d=max(t[ls(x)].d,t[rs(x)].d);
    t[x].d=max(t[x].d,t[rs(x)].maxx-t[ls(x)].minn);
    t[x]._d=max(t[ls(x)]._d,t[rs(x)]._d);
    t[x]._d=max(t[x]._d,t[ls(x)].maxx-t[rs(x)].minn);
}

inline void push_down(int x)
{
    if(t[x].tag)
    {
        t[ls(x)].tag+=t[x].tag,t[ls(x)].maxx+=t[x].tag,t[ls(x)].minn+=t[x].tag;
        t[rs(x)].tag+=t[x].tag,t[rs(x)].maxx+=t[x].tag,t[rs(x)].minn+=t[x].tag;
        t[x].tag=0;
    }
}

inline void build(int x,int l,int r)
{
    if(l==r){t[x].minn=t[x].maxx=wt[l];t[x].d=t[x]._d=0;return;}
    int mid=(l+r)>>1;
    build(ls(x),l,mid);
    build(rs(x),mid+1,r);
    push_up(x);
}

inline void update(int x,int l,int r,int ll,int rr,long long k)
{
    if(ll<=l&&r<=rr)
    {
        t[x].maxx+=k,t[x].minn+=k,t[x].tag+=k;
        return;
    }
    int mid=(l+r)>>1;
    push_down(x);
    if(ll<=mid) update(ls(x),l,mid,ll,rr,k);
    if(mid<rr)  update(rs(x),mid+1,r,ll,rr,k);
    push_up(x);
}

inline void UPDATE(int x,int y,long long k)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        update(1,1,n,dfn[top[x]],dfn[x],k);
        x=fa[top[x]];
    }
    if(dep[x]<dep[y]) swap(x,y);
    update(1,1,n,dfn[y],dfn[x],k);
}

inline Node query(int x,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr) return t[x];
    int mid=(l+r)>>1;
    push_down(x);
    Node cur1,cur2,cur3;
    cur1.d=cur1._d=0,cur1.maxx=-INF,cur1.minn=INF;
    cur2.d=cur2._d=0,cur2.maxx=-INF,cur2.minn=INF;
    cur3.d=cur3._d=0,cur3.maxx=-INF,cur3.minn=INF;
    if(ll<=mid) cur2=query(ls(x),l,mid,ll,rr);
    if(mid<rr) cur3=query(rs(x),mid+1,r,ll,rr);
    cur1.maxx=max(cur2.maxx,cur3.maxx);
    cur1.minn=min(cur2.minn,cur3.minn);
    cur1.d=max(cur2.d,cur3.d);
    cur1._d=max(cur2._d,cur3._d);
    if(ll<=mid&&mid<rr) 
        cur1.d=max(cur1.d,cur3.maxx-cur2.minn),cur1._d=max(cur1._d,cur2.maxx-cur3.minn);
    return cur1;
}

inline long long QUERY(int x,int y)
{
    int cnt1=0,cnt2=0;long long ans=-INF,now_min=INF,now_max=-INF;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
        {
            Node cur=query(1,1,n,dfn[top[x]],dfn[x]);
            ans=max(ans,cur._d);
            p_max[++cnt1]=cur.maxx,p_min[cnt1]=cur.minn;
            x=fa[top[x]];
        }
        else
        {
            Node cur=query(1,1,n,dfn[top[y]],dfn[y]);
            ans=max(ans,cur.d);
            q_max[++cnt2]=cur.maxx,q_min[cnt2]=cur.minn;
            y=fa[top[y]];
        }
    }
    if(dep[x]<dep[y])
    {
        Node cur=query(1,1,n,dfn[x],dfn[y]);
        ans=max(ans,cur.d);
        q_max[++cnt2]=cur.maxx,q_min[cnt2]=cur.minn;
    }
    else
    {
        Node cur=query(1,1,n,dfn[y],dfn[x]);
        ans=max(ans,cur._d);
        p_max[++cnt1]=cur.maxx,p_min[cnt1]=cur.minn;
    }
    for(int i=cnt2;i>=1;i--) p_max[++cnt1]=q_max[i],p_min[cnt1]=q_min[i];
    for(int i=1;i<=cnt1;i++) ans=max(ans,p_max[i]-now_min),now_min=min(now_min,p_min[i]);
    for(int i=cnt1;i>=1;i--) ans=max(ans,now_max-p_min[i]),now_max=max(now_max,p_max[i]);
    return ans;
}

inline void dfs1(int x,int pre)
{
    fa[x]=pre;
    dep[x]=dep[pre]+1;
    siz[x]=1;
    long long maxx=-1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==pre) continue;
        dfs1(v,x);
        siz[x]+=siz[v];
        if(siz[v]>maxx) maxx=siz[v],son[x]=v;
    }
}

inline void dfs2(int x,int topf)
{
    top[x]=topf;
    dfn[x]=++cnt;
    wt[cnt]=w[x];
    if(son[x]) dfs2(son[x],topf);
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==fa[x]||v==son[x]) continue;
        dfs2(v,v);
    }
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce1.in","r",stdin);
    freopen("ce.out","w",stdout);
    #endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1,1);
    dfs2(1,1);
    build(1,1,n);
    /*for(int i=1;i<=n;i++) printf("son[%d]=%d\n",i,son[i]);puts("");
    for(int i=1;i<=n;i++) printf("top[%d]=%d\n",i,top[i]);puts("");
    for(int i=1;i<=n;i++) printf("dfn[%d]=%d\n",i,dfn[i]);puts("");*/
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;long long w;
        scanf("%d%d%lld",&u,&v,&w);
        printf("%lld\n",QUERY(u,v));
        UPDATE(u,v,w);
    }
    return 0;
}
posted @ 2019-02-11 16:24  风浔凌  阅读(127)  评论(0编辑  收藏  举报