標籤:
4127: AbsTime Limit: 40 Sec Memory Limit: 256 MB
Submit: 381 Solved: 132
[Submit][Status][Discuss]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 4Sample Output 10
13
9HINT
對於100%的資料,n,m <= 10^5 且 0<= d,|a_i|<= 10^8
SourceSolution
樹鏈剖分顯然,把樹上路徑問題轉化為序列問題
然後線段樹維護區間權值絕對值和,支援區間加
注意Delta>=0這個條件,即1操作保證加數不為負,即實際值不發生減小
於是線段樹維護一些東西:
l,r左右端點;maxf區間最大負數;num區間正數個數-負數個數;tag區間加的標記;sum區間絕對值和
maxf的意義在於,對於區間加Delta,那麼如果maxf+Delta<0很顯然1操作後會出現變號的情況,對於維護絕對值和必然會做出影響,所以用來進行判斷
num的意義在於計算sum的變化,這裡同樣可以考慮維護正數個數和負數個數,但Code起來比較不方便
tag的意義在於,如果區間+Delta不發生變號情況(即maxf+Delta<0||maxf>=0)的時候,可以直接打上標記,否則則需要把標記下放至分葉節點,在向上更新答案
Code
#include<iostream>#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>using namespace std;int read(){ int x=0,f=1; char ch=getchar(); while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();} while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();} return x*f;}#define maxn 110000int n,m,a[maxn];struct Edgenode{int to,next;}edge[maxn<<1];int head[maxn],cnt=1;void add(int u,int v){cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}void insert(int u,int v){add(u,v); add(v,u);}//----------------------------------------------------------------------------------int size[maxn],fa[maxn],deep[maxn],son[maxn],pl[maxn],sz,pre[maxn],top[maxn],pr[maxn];void dfs_1(int now){ size[now]=1; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=fa[now]) { fa[edge[i].to]=now; deep[edge[i].to]=deep[now]+1; dfs_1(edge[i].to); if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to; size[now]+=size[edge[i].to]; }}void dfs_2(int now,int chain){ pl[now]=++sz; pre[sz]=a[now]; top[now]=chain; if (son[now]) dfs_2(son[now],chain); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=son[now] && edge[i].to!=fa[now]) dfs_2(edge[i].to,edge[i].to); pr[now]=sz;}//----------------------------------------------------------------------------------struct TreeNode{ int l,r;long long maxf,tag,sum,num; void Add(int k) { if (k<0) maxf=k,sum=-k,num=-1; else maxf=0,sum=k,num=1; tag=0; }}tree[maxn<<2];long long Maxf(long long x,long long y){ if (x>=0 && y>=0) return 0; if (x>=0 || y>=0) return min(x,y); return max(x,y);}void Update(int now){ tree[now].maxf=Maxf(tree[now<<1].maxf,tree[now<<1|1].maxf); tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum; tree[now].num=tree[now<<1].num+tree[now<<1|1].num;}void BuildTree(int now,int l,int r){ tree[now].l=l,tree[now].r=r; if (l==r) {tree[now].Add(pre[l]); return;} int mid=(l+r)>>1; BuildTree(now<<1,l,mid); BuildTree(now<<1|1,mid+1,r); Update(now);}void Pushdown(int now){ if (!tree[now].tag || tree[now].l==tree[now].r) return; int tag=tree[now].tag; tree[now].tag=0; tree[now<<1].maxf+=tag; tree[now<<1].sum+=tree[now<<1].num*tag; tree[now<<1].tag+=tag; tree[now<<1|1].maxf+=tag; tree[now<<1|1].sum+=tree[now<<1|1].num*tag; tree[now<<1|1].tag+=tag;}void Change(int now,int L,int R,int D){ int l=tree[now].l,r=tree[now].r; if (L<=l && R>=r && (tree[now].maxf>=0 || tree[now].maxf+D<0)) {tree[now].maxf+=D; tree[now].sum+=(long long)tree[now].num*D; tree[now].tag+=D; return;} if (l==r) {tree[now].Add(tree[now].maxf+D); return;} Pushdown(now); int mid=(l+r)>>1; if (L<=mid) Change(now<<1,L,R,D); if (R>mid) Change(now<<1|1,L,R,D); Update(now);}long long Query(int now,int L,int R){ Pushdown(now); int l=tree[now].l,r=tree[now].r; if (L<=l && R>=r) return tree[now].sum; int mid=(l+r)>>1; long long re=0; if (L<=mid) re+=Query(now<<1,L,R); if (R>mid) re+=Query(now<<1|1,L,R); return re;}void DeBug(int now){ int l=tree[now].l,r=tree[now].r; if (l==r) {printf("l==r=%d Val=%d maxf=%lld tag=%lld sum=%lld num=%lld\n",l,a[l],tree[now].maxf,tree[now].tag,tree[now].sum,tree[now].num);return;} int mid=(l+r)>>1; DeBug(now<<1); DeBug(now<<1|1); }//----------------------------------------------------------------------------------void Solve_1(int x,int y,int D){ while (top[x]!=top[y]) { if (deep[top[x]]<deep[top[y]]) swap(x,y); Change(1,pl[top[x]],pl[x],D); x=fa[top[x]]; } if (deep[x]>deep[y]) swap(x,y); Change(1,pl[x],pl[y],D);}long long Solve_2(int x,int y){ long long re=0; while (top[x]!=top[y]) { if (deep[top[x]]<deep[top[y]]) swap(x,y); re+=Query(1,pl[top[x]],pl[x]); x=fa[top[x]]; } if (deep[x]>deep[y]) swap(x,y); re+=Query(1,pl[x],pl[y]); return re;}//----------------------------------------------------------------------------------int main(){// freopen("4127.in","r",stdin);// freopen("4127.out","w",stdout); n=read(),m=read(); for (int i=1; i<=n; i++) a[i]=read(); for (int u,v,i=1; i<=n-1; i++) u=read(),v=read(),insert(u,v); dfs_1(1); dfs_2(1,1); BuildTree(1,1,n); int opt,u,v,w; while (m--) { opt=read(); // DeBug(1); if (opt==1) u=read(),v=read(),w=read(),Solve_1(u,v,w); else u=read(),v=read(),printf("%lld\n",Solve_2(u,v)); } return 0;}
這道破題,兩天前YveH和Etienne寫了半天多,DCrusher大爺嘲諷他們,我替他們不服,然後自己果斷寫了1小時,然後調了3小時....發現自信寫不錯的鏈剖少打了一句話MDZZ
(加上省隊集訓,第三題暴力打到70%就去吃飯了,回來懶得打了,體驗了連續滾粗的快感)
發現自己的常數已經接近Etienne了...就慢個100ms不到
【BZOJ-4127】Abs 樹鏈剖分 + 線段樹 (有趣的姿勢)