=== ===
这里放传送门
=== ===
题解
这题不知道是ATP写麻烦了还是它本来就这么麻烦。。一开始看到这题的时候以为挺简单的:正数负数分开维护不就好了嘛!然后开心地写啊写写到pushdown的时候发现woc维护区间和的话就要知道正数和负数分别的增量,也就是要知道这个区间内有多少个正数多少个负数。。那也就要知道每次操作以后有多少负数变成了正数。然而这个题很重要的一点提示就是增量d一定是正数,那么每次只会有负数不停地变成正数,并且每个数最多变化一次。那么只要维护负数的最大值和它出现的位置,每次暴力修改就可以了。注意修改的顺序,应该先修改那些原来就是正数的,再查出由负数变成正数的,再更改剩下的负数,这样才不会出现重复标记的问题。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 2000000000
using namespace std;
int n,m,v[100010],w[100010],num[100010],deep[100010],top[100010],f[500010],size[100010];
int p[100010],a[200010],next[200010],tot,cnt,fa[100010],son[100010],Maxpos[500010];
long long ndlt[500010],pdlt[500010],pos[500010],neg[500010],Max[500010];
void add(int x,int y){
tot++;a[tot]=y;next[tot]=p[x];p[x]=tot;
}
long long abs(long long x){return (x>0)?x:-x;}
void dfs(int u){
deep[u]=deep[fa[u]]+1;
size[u]=1;son[u]=0;
for (int i=p[u];i!=0;i=next[i])
if (a[i]!=fa[u]){
int v=a[i];
fa[v]=u;dfs(v);
size[u]+=size[v];
if (size[v]>size[son[u]]) son[u]=v;
}
}
void dfs_again(int u,int tp){
top[u]=tp;w[u]=++cnt;num[cnt]=v[u];
if (son[u]!=0) dfs_again(son[u],tp);
for (int i=p[u];i!=0;i=next[i])
if (a[i]!=fa[u]&&a[i]!=son[u])
dfs_again(a[i],a[i]);
}
void update(int i){
pos[i]=pos[i<<1]+pos[(i<<1)+1];
neg[i]=neg[i<<1]+neg[(i<<1)+1];
if (Max[i<<1]>Max[(i<<1)+1]){
Max[i]=Max[i<<1];Maxpos[i]=Maxpos[i<<1];
}//以Max为基础更新Maxpos,注意当数值相同的时候位置靠后的优先
else{
Max[i]=Max[(i<<1)+1];Maxpos[i]=Maxpos[(i<<1)+1];
}
f[i]=f[i<<1]+f[(i<<1)+1];
}
void build(int i,int l,int r){
if (l==r){
Maxpos[i]=l;
if (num[l]<0){//正数和负数分开处理
neg[i]=Max[i]=num[l];f[i]=1;}
else {
pos[i]=num[l];num[l]=-inf;Max[i]=num[l];}
return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build((i<<1)+1,mid+1,r);
update(i);
}
void Padd(int i,int l,int r,long long v){
long long T=r-l+1;
if (f[i]==T) return;//如果这个区间里没有正数就返回
pdlt[i]+=v;
pos[i]+=v*(T-f[i]);
}
void Nadd(int i,int l,int r,long long v){
if (neg[i]==0) return;//如果没有负数就返回
ndlt[i]+=v;neg[i]+=v*f[i];Max[i]+=v;
}
void pushdown(int i,int l,int r){
if (pdlt[i]!=0){
int mid=(l+r)>>1;
Padd(i<<1,l,mid,pdlt[i]);
Padd((i<<1)+1,mid+1,r,pdlt[i]);
pdlt[i]=0;
}
if (ndlt[i]!=0){
int mid=(l+r)>>1;
Nadd(i<<1,l,mid,ndlt[i]);
Nadd((i<<1)+1,mid+1,r,ndlt[i]);
ndlt[i]=0;
}
}
void maxchange(int i,int l,int r,int x,int v){
if (l==r){
pos[i]+=v;neg[i]=0;//v代表这个负数加成正数以后的数值
num[l]=Max[i]=-inf;//消除的这个负数不能让它再成为最大值
f[i]--;return;
}
int mid=(l+r)>>1;
pushdown(i,l,r);
if (x<=mid) maxchange(i<<1,l,mid,x,v);
else maxchange((i<<1)+1,mid+1,r,x,v);
update(i);
}
long long askmax(int i,int l,int r,int left,int right,long long &X){
if (left<=l&&right>=r){
X=max(X,Max[i]);//顺便维护最大值
return Maxpos[i];
}
int mid=(l+r)>>1;
long long lc,rc;
pushdown(i,l,r);lc=rc=0;
if (left<=mid) lc=askmax(i<<1,l,mid,left,right,X);
if (right>mid) rc=askmax((i<<1)+1,mid+1,r,left,right,X);
if (lc==0) return rc;if (rc==0) return lc;
if (Max[i<<1]>Max[(i<<1)+1]) return lc;//从左右子树的答案里选择一个更优的
return rc;
}
void Tadd(int i,int l,int r,long long v){
long long Pos,wer=-inf;
Padd(i,l,r,v);//先更改那些原本就是正数的
Pos=askmax(1,1,n,l,r,wer);//查询最大负数
while (wer!=-inf&&wer+v>=0){
maxchange(1,1,n,Pos,wer+v);
wer=-inf;Pos=askmax(1,1,n,l,r,wer);
}
Nadd(i,l,r,v);//更改剩下的负数
}
void add(int i,int l,int r,int left,int right,int d){
if (left<=l&&right>=r){
Tadd(i,l,r,d);return;
}
int mid=(l+r)>>1;
pushdown(i,l,r);
if (left<=mid) add(i<<1,l,mid,left,right,d);
if (right>mid) add((i<<1)+1,mid+1,r,left,right,d);
update(i);
}
long long askval(int i,int l,int r,int left,int right){
if (left<=l&&right>=r) return pos[i]-neg[i];
int mid=(l+r)>>1;
long long ans=0;
pushdown(i,l,r);
if (left<=mid) ans+=askval(i<<1,l,mid,left,right);
if (right>mid) ans+=askval((i<<1)+1,mid+1,r,left,right);
return ans;
}
void change(int x,int y,int d){
while (top[x]!=top[y]){
if (deep[top[x]]<deep[top[y]]) swap(x,y);
add(1,1,n,w[top[x]],w[x],d);
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
add(1,1,n,w[x],w[y],d);
}
long long query(int x,int y){
long long ans=0;
while (top[x]!=top[y]){
if (deep[top[x]]<deep[top[y]]) swap(x,y);
ans+=askval(1,1,n,w[top[x]],w[x]);
x=fa[top[x]];
}
if (deep[x]>deep[y]) swap(x,y);
ans+=askval(1,1,n,w[x],w[y]);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&v[i]);
for (int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(1);dfs_again(1,1);
build(1,1,n);
for (int i=1;i<=m;i++){
int k;scanf("%d",&k);
if (k==1){
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
change(u,v,d);
}else{
int u,v;
scanf("%d%d",&u,&v);
printf("%I64d\n",query(u,v));
}
}
return 0;
}
看到这里的人真是感谢你能受得了上面那一坨又丑又长的东西〒▽〒。。