Description
给定一棵树,设计数据结构支持以下操作
1 u v d 表示将路径 (u,v) 加d
2 u v 表示询问路径 (u,v) 上点权绝对值的和
Input
第一行两个整数n和m,表示结点个数和操作数
接下来一行n个整数a_i,表示点i的权值
接下来n-1行,每行两个整数u,v表示存在一条(u,v)的边
接下来m行,每行一个操作,输入格式见题目描述
Output
对于每个询问输出答案
Sample Input
4 4
-4 1 5 -2
1 2
2 3
3 4
2 1 3
1 1 4 3
2 1 3
2 3 4
Sample Output
10
13
9
HINT
对于100%的数据,n,m <= 10^5 且 0<= d,|a_i|<= 10^8
Source
SDOIRound2之前模拟赛zky学长出的题
当时考场上想出了标算但是写残了TAT
如今终于不写残了
当时感觉有点像花神游历各国所以考虑记录区间最大负整数然后暴力下放之类的balabala
然而正解并不是这个
其实正解是整体二分二分每个点大于零的时间然后树剖
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
#define ln rt<<1
#define rn rt<<1|1
#define MAXN 100010
#define MAXINT 0x7fffffff
#define LL long long
#define GET (ch>='0'&&ch<='9')
using namespace std;
int n,m,top,Top;
int val[MAXN],size[MAXN],deep[MAXN],chain[MAXN],num[MAXN],Val[MAXN],fa[MAXN];
struct seg
{
int l,r;
LL sum,maxn,delta,flag;
void insert(int x)
{
if (x<0) maxn=x,sum=-x,flag=-1;
else maxn=0,sum=x,flag=1;delta=0;
}
}tree[MAXN<<2];
struct edge
{
int to;
edge *next;
}e[MAXN<<1],*prev[MAXN];
void insert(int u,int v) {e[++top].to=v;e[top].next=prev[u];prev[u]=&e[top];}
void in(int &x)
{
char ch=getchar();x=0;int flag=1;
while (!GET) flag=ch=='-'?-1:1,ch=getchar();
while (GET) x=x*10+ch-'0',ch=getchar();x*=flag;
}
void dfs1(int x,int f)
{
size[x]=1;fa[x]=f;
for (edge *i=prev[x];i;i=i->next) if (i->to!=f) deep[i->to]=deep[x]+1,dfs1(i->to,x),size[x]+=size[i->to];
}
void dfs2(int x,int last)
{
chain[x]=last;num[x]=++Top;Val[num[x]]=val[x];int t=0;
for (edge *i=prev[x];i;i=i->next) if (deep[i->to]>deep[x]&&size[i->to]>size[t]) t=i->to;
if (!t) return;dfs2(t,last);
for (edge *i=prev[x];i;i=i->next) if (deep[i->to]>deep[x]&&i->to!=t) dfs2(i->to,i->to);
}
LL Min(LL a,LL b)
{
if (a>=0&&b>=0) return 0;
if (a>=0||b>=0) return min(a,b);
return max(a,b);
}
void push_up(int rt)
{
tree[rt].maxn=Min(tree[ln].maxn,tree[rn].maxn);
tree[rt].sum=tree[ln].sum+tree[rn].sum;
tree[rt].flag=tree[ln].flag+tree[rn].flag;
}
void push_down(int rt)
{
if (tree[rt].l==tree[rt].r||tree[rt].delta==0) return;
tree[ln].maxn+=tree[rt].delta;tree[ln].sum+=tree[ln].flag*tree[rt].delta;tree[ln].delta+=tree[rt].delta;
tree[rn].maxn+=tree[rt].delta;tree[rn].sum+=tree[rn].flag*tree[rt].delta;tree[rn].delta+=tree[rt].delta;
tree[rt].delta=0;
}
void build(int rt=1,int l=1,int r=n)
{
tree[rt].l=l;tree[rt].r=r;
if (l==r) {tree[rt].insert(Val[l]);return;}
int mid=(l+r)>>1;build(lchild);build(rchild);push_up(rt);
}
void modify(int rt,int l,int r,int delta)
{
int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1;
if (l<=L&&r>=R&&(tree[rt].maxn>=0||tree[rt].maxn+delta<0)) {tree[rt].maxn+=delta;tree[rt].sum+=(LL)tree[rt].flag*delta;tree[rt].delta+=delta;return;}
if (L==R) {tree[rt].insert(tree[rt].maxn+delta);return;}
push_down(rt);
if (r<=mid) modify(ln,l,r,delta);
else if (l>mid) modify(rn,l,r,delta);
else modify(ln,l,mid,delta),modify(rn,mid+1,r,delta);
push_up(rt);
}
LL query(int rt,int l,int r)
{
push_down(rt);
int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1;
if (l<=L&&r>=R) return tree[rt].sum;
if (r<=mid) return query(ln,l,r);
if (l>mid) return query(rn,l,r);
return query(ln,l,mid)+query(rn,mid+1,r);
}
void Modify(int a,int b,int delta)
{
while (chain[a]!=chain[b])
{
if (deep[chain[a]]<deep[chain[b]]) swap(a,b);
modify(1,num[chain[a]],num[a],delta);a=fa[chain[a]];
}
if (deep[a]<deep[b]) swap(a,b);modify(1,num[b],num[a],delta);
}
LL Query(int a,int b)
{
LL ret=0;
while (chain[a]!=chain[b])
{
if (deep[chain[a]]<deep[chain[b]]) swap(a,b);
ret+=query(1,num[chain[a]],num[a]);a=fa[chain[a]];
}
if (deep[a]<deep[b]) swap(a,b);ret+=query(1,num[b],num[a]);return ret;
}
int main()
{
in(n);in(m);int opt,u,v,d;
for (int i=1;i<=n;i++) in(val[i]);
for (int i=1;i<n;i++) in(u),in(v),insert(u,v),insert(v,u);
dfs1(1,0);dfs2(1,1);build();
while (m--)
{
in(opt);in(u);in(v);
if (opt==1) in(d),Modify(u,v,d);
else printf("%lld\n",Query(u,v));
}
}