SPOJ GSS7 Can you answer these queries VII 树链剖分+线段树(方向)

https://www.spoj.com/problems/GSS7/en/

题意翻译
题目描述

给定一棵树,有N(N≤100000)N(N \le 100000)N(N≤100000)个节点,每一个节点都有一个权值xi(∣xi∣≤10000)x_i (|x_i| \le 10000)xi​(∣xi​∣≤10000)

你需要执行Q(Q≤100000)Q (Q \le 100000)Q(Q≤100000)次操作:

1 a b 查询(a,b)这条链上的最大子段和,可以为空(即输出000)
2 a b c 将(a,b)这条链上的所有点权变为c (∣c∣<=10000)(|c| <= 10000)(∣c∣<=10000)

输入格式:

第一行一个整数NNN

接下来一行有NNN个整数表示xix_ixi​

接下来N−1N-1N−1行,每行两个整数u,vu,vu,v表示uuu和vvv之间有一条边相连

接下来一行一个整数QQQ

之后有QQQ行,每行诸如1 a b或者2 a b c
输出格式

对于每一个询问,输出答案
输入样例

5
-3 -2 1 2 3
1 2
2 3
1 4
4 5
3
1 2 5
2 3 4 2
1 2 5

输出样例

5
9

题目描述

Given a tree with N (N <= 100000) nodes. Each node has a interger value x_i (|x_i| <= 10000).

You have to apply Q (Q <= 100000) operations:

  1. 1 a b : answer the maximum contiguous sum (maybe empty,will always larger than or equal to 0 ) from the path a->b ( inclusive ).

  2. 2 a b c : change all value in the path a->b ( inclusive ) to c. (|c| <= 10000)
    输入格式

first line consists one interger N.

next line consists N interger x_i.

next N-1 line , each consists two interger u,v , means that node u and node v are connected

next line consists 1 interger Q.

next Q line : 1 a b or 2 a b c .
输出格式

For each query, output one line the maximum contiguous sum.
输入输出样例
输入 #1

5
-3 -2 1 2 3
1 2
2 3
1 4
4 5
3
1 2 5
2 3 4 2
1 2 5

输出 #1

5
9
思路:树剖是很明显的,关键就是怎么用线段树维护最大连续子段和以及如何在链与链合并时维护信息,不过用线段树维护最大连续子段和算是一个模板了,不会的请戳。(建议看一下,不然下面你估计看不懂 )其实链与链的合并与线段树区间与区间的合并在本质上是一样的,因此我们完全可以套用线段树的pushup操作来完成链与链合并时的维护操作。当然还是有坑点的,熟悉树剖的应该明白对于某些问题链与链合并的时候其实是有方向(顺序)的,我们不妨设节点u在节点v的左侧且深度要大于节点v,下面看张图:(其他情况大家可自行分析)
在这里插入图片描述
假设上面四条链就是我们要合并的,现在通过查询得到了左边第二条链tmp1的信息,L是路径上tmp1前所有链合并后的信息,那么可直接L=Merge(tmp1,L),同理右侧有R=Merge(tmp2,R),注意这个顺序不能反过来,因为要把当前查询到的当做左区间,该链向下的当做右区间。最后合并L、R的时候还有一个细节,就是我们要交换L的begl和begr的值,因为之前是L的左区间端点和R的左区间端点相邻,这样合并得到的结果是不对的~

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=1e5+5;

struct node
{
    int l,r,sum,begl,begr,ans,lazy;
    node()
    {
        sum=begl=begr=ans=0;
    }
}tree[maxn<<2];

int siz[maxn];//子树大小
int son[maxn];//重儿子
int fa[maxn];//父节点
int deep[maxn];//深度
int top[maxn];//所在链链顶
int pos[maxn];//dfs序编号
int v[maxn];//映射到序列后的值 线段树依照此序列建树
int a[maxn];//树上各节点的值
vector<int> vec[maxn];//存边
int n,m,tot=0;

void dfs1(int u,int f)//处理出重儿子 深度 父亲 子树大小等信息
{
    siz[u]=1;
    son[u]=0;
    fa[u]=f;
    deep[u]=deep[f]+1;
    int to;
    for(int i=0;i<vec[u].size();i++)
    {
        to=vec[u][i];
        if(to!=f)
        {
            dfs1(to,u);
            siz[u]+=siz[to];
            if(siz[son[u]]<siz[to])
                son[u]=to;
        }
    }
}

void dfs2(int u,int f,int k)//处理出 链顶 dfs序 映射后的值 等信息
{
    top[u]=k;
    pos[u]=++tot;
    v[tot]=a[u];
    if(son[u])
        dfs2(son[u],u,k);
    int to;
    for(int i=0;i<vec[u].size();i++)
    {
        to=vec[u][i];
        if(to!=f&&to!=son[u])
            dfs2(to,u,to);
    }
}

inline node Merge(node l,node r)
{
    node ans;
    ans.sum=l.ans+r.ans;
    ans.begl=max(l.begl,l.sum+r.begl);
    ans.begr=max(r.begr,r.sum+l.begr);
    ans.ans=max(l.begr+r.begl,max(l.ans,r.ans));
    return ans;
}

inline void up(int i)
{
    int l=i<<1,r=i<<1|1;
    tree[i].sum=tree[l].sum+tree[r].sum;
    tree[i].begl=max(tree[l].begl,tree[l].sum+tree[r].begl);
    tree[i].begr=max(tree[r].begr,tree[r].sum+tree[l].begr);
    tree[i].ans=max(tree[l].begr+tree[r].begl,max(tree[l].ans,tree[r].ans));
}

inline void down(int i)
{
    int l=i<<1,r=i<<1|1;
    tree[l].lazy=tree[r].lazy=tree[i].lazy;
    tree[l].sum=(tree[l].r-tree[l].l+1)*tree[i].lazy;
    tree[r].sum=(tree[r].r-tree[r].l+1)*tree[i].lazy;
    tree[l].begl=tree[l].begr=tree[l].ans=max(tree[l].sum,0);
    tree[r].begl=tree[r].begr=tree[r].ans=max(tree[r].sum,0);
    tree[i].lazy=INF;
}

inline void build(int i,int l,int r)
{
    tree[i].lazy=INF;
    tree[i].l=l,tree[i].r=r;
    if(l==r)
    {
        tree[i].sum=v[l];
        tree[i].ans=tree[i].begl=tree[i].begr=max(v[l],0);
        return ;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    up(i);
}

void update(int i,int l,int r,int v)
{
    if(l==tree[i].l&&r==tree[i].r)
    {
        tree[i].lazy=v;
        tree[i].sum=(r-l+1)*v;
        if(v>=0)
            tree[i].begl=tree[i].begr=tree[i].ans=tree[i].sum;
        else
            tree[i].begl=tree[i].begr=tree[i].ans=v;
        return ;
    }
    if(tree[i].lazy!=INF)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(r<=mid)
        update(i<<1,l,r,v);
    else if(l>mid)
        update(i<<1|1,l,r,v);
    else
        update(i<<1,l,mid,v),
        update(i<<1|1,mid+1,r,v);
    up(i);
}

node query(int i,int l,int r)
{
    if(l==tree[i].l&&r==tree[i].r)
        return tree[i];
    if(tree[i].lazy!=INF)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(r<=mid)
        return query(i<<1,l,r);
    else if(l>mid)
        return query(i<<1|1,l,r);
    else
    {
        node lc=query(i<<1,l,mid);
        node rc=query(i<<1|1,mid+1,r);
        node tmp;
        tmp.sum=lc.sum+rc.sum;
        tmp.begl=max(lc.begl,lc.sum+rc.begl);
        tmp.begr=max(rc.begr,rc.sum+lc.begr);
        tmp.ans=max(lc.begr+rc.begl,max(lc.ans,rc.ans));
        return tmp;
    }
}

inline void updatework(int u,int v,int val)
{
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]])
            swap(u,v);
        update(1,pos[top[u]],pos[u],val);
        u=fa[top[u]];
    }
    if(deep[u]>deep[v])
        swap(u,v);
    update(1,pos[u],pos[v],val);
}

inline int solve(int u,int v)
{
    int ans=0;
    node l,r;
    node tmp;
    while(top[u]!=top[v])
    {
        if(deep[top[u]]>deep[top[v]])
        {
            tmp=query(1,pos[top[u]],pos[u]);
            l=Merge(tmp,l);//顺序不能反
            u=fa[top[u]];
        }
        else
        {
            tmp=query(1,pos[top[v]],pos[v]);
            r=Merge(tmp,r);//顺序不能反
            v=fa[top[v]];
        }
    }
    if(deep[u]>deep[v])
        l=Merge(query(1,pos[v],pos[u]),l);
    else
        r=Merge(query(1,pos[u],pos[v]),r);
    swap(l.begl,l.begr);//方向
    return Merge(l,r).ans;
}

inline void prework()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int u,v;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        vec[u].push_back(v);
        vec[v].push_back(u);
    }
    dfs1(1,0);
    dfs2(1,0,1);
    build(1,1,n);
}

inline void mainwork()
{
    scanf("%d",&m);
    int op,u,v;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&op,&u,&v);
        if(op==1)
            printf("%d\n",solve(u,v));
        else
        {
            scanf("%d",&op);
            updatework(u,v,op);
        }
    }
}

int main()
{
    prework();
    mainwork();
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值