[LOJ139]-樹鏈剖分
【題目地址】
其實這個題不是個單純的模板,所以要做單純無腦的模板或者學習樹鏈剖分的可以去這裡【Luogu-樹鏈剖分】,這裡不會講樹鏈剖分的原理與實現。
開始想寫一些模板題目,luogu的寫得差不多了,所以就去loj看看,結果就看到了這道,emmmm
題目大意
給你一棵有點權的樹,開始時根為1號點,請你實現以下操作:
- 換根
- 一條鏈上點權加
- 一個子樹內點權加
- 詢問一條鏈上點權的和
- 詢問一個子樹內的點權和
操作大多數和luogu的模板題一樣,且不用取模,但是多了一個換根操作。
所以對於鏈上的操作是不會影響的,只有子樹部分操作有影響。
首先考慮暴力,每次換根後重構,那麼複雜度顯然接受不了,所以我們要考慮不重構,所以開始的時候就以一號點為根,先把樹剖了,線段樹建出來。
此時,我們就要考慮不同的根和子樹操作該如何實現,我們假設根為,操作子樹的根節點為,那麼有如下幾種情況(原來的子樹是指的在根為1時的子樹):
- 當,此時操作範圍就是整個樹,將整個樹加或者求和即可。
- 當不在的原來的子樹裡面時,操作範圍就是原來的子樹。
- 當在的子樹內時,情況就比較複雜,操作範圍就為整個樹減去到路徑上深度最小的點(不包含點)的原來的子樹。
比如上面這個例子就是,我們現在6號點為根,查詢2號點,它的子樹就是1,3,5,也就是原來的整個樹減去4號點的原來的子樹剩餘部分,而4號點剛好是上不包含2的深度最小的點。
所以我們再子樹操作的時候再稍微判斷一下,分類討論求一下就好啦,求路上深度最小的點可以倍增往上跳,也可以直接樹剖的線段樹維護即可。
長長的程式碼:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e5+10;
const int inf=1e9;
int n,m,lg;
ll val[M];
struct ss{
int to,last;
ss(){}
ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
g[++cnt]=ss(b,head[a]);head[a]=cnt;
g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
int dep[M],sze[M],top[M],son[M],f[M];
int num[M],rf[M],tim,root=1;
void dfs1(int a){
sze[a]=1;
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]) continue;
dep[g[i].to]=dep[a]+1;
f[g[i].to]=a;
dfs1(g[i].to);
sze[a]+=sze[g[i].to];
if(!son[a]||sze[son[a]]<sze[g[i].to])
son[a]=g[i].to;
}
}
void dfs2(int a,int b){
top[a]=b;rf[num[a]=++tim]=a;
if(!son[a]) return;
dfs2(son[a],b);
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]||g[i].to==son[a]) continue;
dfs2(g[i].to,g[i].to);
}
}
ll sum[M<<2],lazy[M<<2];
int minp[M<<2];
int tmin(int a,int b){
if(a==inf)return b;
if(b==inf)return a;
if(dep[a]<dep[b])return a;
else return b;
}
void pushup(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
minp[o]=tmin(minp[o<<1],minp[o<<1|1]);
}
void pushdown(int o,int l,int r,int mid){
if(!lazy[o]) return;
lazy[o<<1]+=lazy[o];
lazy[o<<1|1]+=lazy[o];
sum[o<<1]+=(mid-l+1)*lazy[o];
sum[o<<1|1]+=(r-mid)*lazy[o];
lazy[o]=0;
}
void build(int o,int l,int r){
if(l==r){
sum[o]=val[rf[l]];
minp[o]=rf[l];
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
void update(int o,int l,int r,int L,int R,ll v){
if(L<=l&&r<=R){
sum[o]+=(r-l+1)*v;
lazy[o]+=v;
return;
}
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(L<=mid) update(o<<1,l,mid,L,R,v);
if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
pushup(o);
}
ll query(int o,int l,int r,int L,int R){
if(L<=l&&r<=R) return sum[o];
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(R<=mid) return query(o<<1,l,mid,L,R);
else if(L>mid) return query(o<<1|1,mid+1,r,L,R);
else return query(o<<1,l,mid,L,R)+query(o<<1|1,mid+1,r,L,R);
}
int find(int o,int l,int r,int L,int R){
if(R<L)return inf;
if(L<=l&&r<=R) return minp[o];
int mid=l+r>>1;
if(R<=mid) return find(o<<1,l,mid,L,R);
else if(L>mid) return find(o<<1|1,mid+1,r,L,R);
else return tmin(find(o<<1,l,mid,L,R),find(o<<1|1,mid+1,r,L,R));
}
int lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
return a;
}
int Upto(int a,int b){
int ans=inf,tw=a;
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
if(top[a]!=b)ans=tmin(ans,find(1,1,n,num[top[a]],num[a]));
else ans=tmin(ans,find(1,1,n,num[top[a]]+1,num[a]));
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
ans=tmin(ans,find(1,1,n,num[a]+1,num[b]));
return ans;
}
int check(int a){
if(a==root) return 0;
int L=lca(a,root);
if(L==a) return Upto(a,root);
else return -1;
}
void Add_Chain(int a,int b,ll w){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
update(1,1,n,num[top[a]],num[a],w);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
update(1,1,n,num[a],num[b],w);
}
ll Ask_Chain(int a,int b){
ll ans=0;
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
ans+=query(1,1,n,num[top[a]],num[a]);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
ans+=query(1,1,n,num[a],num[b]);
return ans;
}
void Add_Tree(int u,ll v){
int type=check(u);
if(!type){
update(1,1,n,1,n,v);
}else if(type>0){
update(1,1,n,1,n,v);
update(1,1,n,num[type],num[type]+sze[type]-1,-v);
}else{
update(1,1,n,num[u],num[u]+sze[u]-1,v);
}
}
ll Ask_Tree(int u){
int type=check(u);
if(!type){
return query(1,1,n,1,n);
}else if(type>0){
ll ans=query(1,1,n,num[type],num[type]+sze[type]-1);
return query(1,1,n,1,n)-ans;
}else{
return query(1,1,n,num[u],num[u]+sze[u]-1);
}
}
int opt,a,b,c;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
for(int i=2;i<=n;i++){
scanf("%d",&a);
add(i,a);
}
dfs1(1);
dfs2(1,1);
build(1,1,n);
for(scanf("%d",&m);m--;){
scanf("%d",&opt);
if(opt==1){
scanf("%d",&a);
root=a;
}else if(opt==2){
scanf("%d%d%d",&a,&b,&c);
Add_Chain(a,b,c);
}else if(opt==3){
scanf("%d%d",&a,&b);
Add_Tree(a,b);
}else if(opt==4){
scanf("%d%d",&a,&b);
printf("%lld\n",Ask_Chain(a,b));
}else{
scanf("%d",&a);
printf("%lld\n",Ask_Tree(a));
}
}
return 0;
}
相關文章
- 樹鏈剖分
- [OI] 樹鏈剖分
- 2024.3.14 樹鏈剖分
- 淺談樹鏈剖分
- 樹鏈剖分總結
- Something about 樹鏈剖分
- 【筆記/模板】樹鏈剖分筆記
- #8. 「模板」樹鏈剖分
- 「學習筆記」樹鏈剖分筆記
- 樹鏈剖分學習筆記筆記
- 對樹鏈剖分的愛 題解
- P8025 【樹鏈剖分求祖先】
- 樹鏈剖分模板+入門題 SPOJ - QTREEQT
- 樹鏈剖分解析
- bzoj4551: [Tjoi2016&Heoi2016]樹(樹鏈剖分)
- 一起來學習樹鏈剖分吧!
- bzoj3531: [Sdoi2014]旅行(樹鏈剖分+線段樹)
- 樹剖 dsu on tree 長鏈
- TZOJ 8472 : Tree (重鏈剖分+線段樹) POJ 3237
- 長鏈剖分模板
- 長鏈剖分筆記筆記
- 迴文樹線上剖分???
- BF的資料結構題單-提高組——樹鏈剖分資料結構
- P1505 [國家集訓隊]旅遊 (樹鏈剖分)
- 熟練剖分(tree) 樹形DP
- bzoj3626: [LNOI2014]LCA(離線處理+樹鏈剖分)
- 重鏈剖分題目選講
- 【2024-ZR-C Day 6】資料結構(4):樹(重、長)鏈剖分、虛樹、dsu on tree資料結構
- 樹剖(不太會)
- P3038 [USACO11DEC]Grass Planting G(樹鏈剖分邊權轉點權)
- 『dfn、樹剖雜項』Day9
- P3979 遙遠的國度(換根樹剖)
- 複雜多邊形的三角剖分
- 點分樹
- 劃分樹
- 淺剖0.1 + 0.2 = ?
- Note - 樹分治(點分治、點分樹)
- 生成樹與鏈路聚合