题目大意: 求树上最大子段和,带修。
如果做了gss系列的前六题,那么这题相信不难。用一个树链剖分维护这棵树即可。
但是细节比较烦。
具体做法:对于求 x x x到 y y y路径上的最大子段和,只需要让 x x x和 y y y往上跑一遍,将经过的链的信息合并在一起即可,但是最后当 x x x和 y y y在一条重链上时,我们需要将 x x x经过的链的信息和 y y y经过的链的信息合并,而此时这两个信息的左端点都朝上,于是可以将其中一个翻转,于是就可以接起来了。
具体细节看代码,会有很清楚的注释 (毕竟自己都觉得上面这个讲的不明白):
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
struct edge{int y,next;};
edge e[maxn*2];
int first[maxn];
int n,m,a[maxn];
void buildroad(int x,int y)
{
static int len=0;
e[++len]=(edge){y,first[x]};
first[x]=len;
}
int fa[maxn],size[maxn],mson[maxn],deep[maxn];
void dfs1(int x)//树剖预处理,找到重儿子,求出深度之类的
{
size[x]=1;
for(int i=first[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa[x])continue;
fa[y]=x;
deep[y]=deep[x]+1;
dfs1(y);
if(size[y]>size[mson[x]])mson[x]=y;
size[x]+=size[y];
}
}
int now[maxn],old[maxn],top[maxn],tot=0;
void dfs2(int x,int tt)//树剖预处理,给每个点重新编号
{
now[x]=++tot;
old[tot]=x;
top[x]=tt;
if(mson[x]!=0)dfs2(mson[x],tt);
for(int i=first[x];i;i=e[i].next)
if(e[i].y!=fa[x]&&e[i].y!=mson[x])dfs2(e[i].y,e[i].y);
}
struct node{
int l,r,sl,sr,ans,sum,lazy;
node *zuo,*you;
void check(node *le,node *ri)//表示将两个点的信息合并到自己身上
{
if(le==NULL||ri==NULL)//如果只有一个点,就直接继承
{
if(le==NULL)sum=ri->sum,sl=ri->sl,sr=ri->sr,ans=ri->ans;
else sum=le->sum,sl=le->sl,sr=le->sr,ans=le->ans;
return;
}
sum=le->sum+ri->sum;
sl=max(le->sl,le->sum+ri->sl);
sr=max(ri->sr,ri->sum+le->sr);
ans=max(le->ans,max(ri->ans,le->sr+ri->sl));
}
node(int x,int y)//建树
{
if(x==0)return;
l=x,r=y;lazy=-23333;
//注意lazy不能为0,因为修改操作是可以为0的,在没有修改操作时lazy的绝对值要大于10000保证不和修改操作冲突
if(l<r)
{
int mid=l+r>>1;
zuo=new node(x,mid);
you=new node(mid+1,y);
check(zuo,you);
}
else sum=sl=sr=ans=a[old[x]],zuo=NULL,you=NULL;
}
node *get(int x,int y)//求x~y的最大子段和
{
pushdown();
if(l==x&&r==y)//如果完全符合,注意不能直接返回自己,要新建一个点继承自己的信息然后返回这个点
{//因为在ask函数中会有将点的信息翻转的操作,如果直接返回自己可能会把自己的信息搞乱
//顺便扯一句,就是因为这个WA了半天
node *re=new node(0,0);//注意不是re=this,不然的话跟返回this没有区别
re->check(this,NULL);//继承信息
return re;
}
int mid=l+r>>1;
if(y<=mid)return zuo->get(x,y);
else if(x>=mid+1)return you->get(x,y);
else
{
node *left=zuo->get(x,mid),*right=you->get(mid+1,y);
node *re=new node(0,0);
re->check(left,right);
return re;
}
}
void pushdown()
{
if(lazy!=-23333)
{
if(lazy>0)sl=sr=sum=ans=(r-l+1)*lazy;
else sl=sr=ans=lazy,sum=(r-l+1)*lazy;
if(zuo!=NULL)zuo->lazy=lazy,you->lazy=lazy;
lazy=-23333;
}
}
void change(int x,int y,int z)//将x~y的值改成z
{
if(l==x&&r==y)
{
lazy=z;
pushdown();
return;
}
pushdown();
int mid=l+r>>1;
if(y<=mid)zuo->change(x,y,z),you->pushdown();
else if(x>=mid+1)you->change(x,y,z),zuo->pushdown();
else zuo->change(x,mid,z),you->change(mid+1,y,z);
check(zuo,you);
}
};
node *root;
void ask(int x,int y)//重头戏在此
{
node *l=NULL,*r=NULL;//*l,*r用来存x和y往上跑时一路上的信息
bool v=false;//v记录x和y时交换过的还是没交换过的,结合代码感性理解(虽然不知道有没有用)
while(top[x]!=top[y])
{
if(deep[top[x]]>deep[top[y]])swap(x,y),swap(l,r),v^=1;//注意这里l和r也要交换
node *wula=root->get(now[top[y]],now[y]);//记录下当前点到重链顶端的答案
if(r==NULL)r=wula;//如果之前没有信息,就直接存
else
{
node *wulala=new node(0,0);
wulala->check(wula,r);//否则将以前的信息和现在的信息合并,注意,这里面一定是(wula,r),而不能是(r,wula)
r=wulala;
}
y=fa[top[y]];
}
if(deep[x]>deep[y])swap(x,y),swap(l,r),v^=1;
node *wula=root->get(now[x],now[y]);//当x和y在同一条链上时,最后求出x到y的最大子段和
if(v)swap(l,r),swap(wula->sl,wula->sr);//最后要让l的右端点对着wula的左端点,wula的右端点对着r的左端点
//这样才能正常的合并
if(l!=NULL)swap(l->sl,l->sr);//注意记得判l是否为NULL
node *wulala=new node(0,0);//最后将三者的信息合并
wulala->check(l,wula);
wula->check(wulala,r);
if(wula->ans>=0)printf("%d\n",wula->ans);//如果小于0,不如不选
else printf("0\n");
}
void change(int x,int y,int z)
{
while(top[x]!=top[y])//这个很简单,跑一遍沿路修改即可
{
if(deep[top[x]]>deep[top[y]])swap(x,y);
root->change(now[top[y]],now[y],z);
y=fa[top[y]];
}
if(deep[x]>deep[y])swap(x,y);
root->change(now[x],now[y],z);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d %d",&x,&y);
buildroad(x,y);
buildroad(y,x);
}
dfs1(1);
dfs2(1,1);
root=new node(1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int id,x,y,z;
scanf("%d",&id);
switch(id)
{
case 1:
scanf("%d %d",&x,&y);
ask(x,y);
break;
case 2:
scanf("%d %d %d",&x,&y,&z);
change(x,y,z);
break;
}
}
}