題目大意:
如題,已知一棵包含n個結點的樹(連通且無環),每個節點上包含乙個數值,需要支援以下操作:
操作1: 格式: 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z
操作2: 格式: 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和
操作3: 格式: 3 x z 表示將以x為根節點的子樹內所有節點值都加上z
操作4: 格式: 4 x 表示求以x為根節點的子樹內所有節點值之和
題目思路:樹鏈剖分的入門題。樹鏈剖分它實際上就是通過兩次dfs得到重兒子,然後得到輕邊和重邊,然後不斷地通過攀爬top使得他們能到一條鏈上。由於通過輕邊往上爬最多爬logn次,所以複雜度logn*logn,通過重兒子得到dfs序可以保證同一條鏈的dfs序在一起,從而方便線段樹操作。而對於子樹,由於他只是優先跑了重兒子,但是還保留著dfs序的性質,即他的子樹還是在他的後邊,所以可以直接通過siz和本身的dfs序來進行子樹修改
以下是**:
#include using namespace std;
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
#define inf 0x3f3f3f3f
const ll maxn = 1e5+5;
ll a[maxn],fa[maxn],dfn[maxn],son[maxn],dep[maxn],siz[maxn],rk[maxn],top[maxn],tot;
vectorv[maxn];
ll n,m,r,p,op,x,y,z;
struct nodet[maxn<<2];
void dfs1(ll u,ll f)
}}void dfs2(ll u,ll tp)
}void build(ll rt,ll l,ll r)
ll mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
t[rt].val=(t[rt<<1].val+t[rt<<1|1].val)%p;
}void spread(ll rt)
}void update(ll rt,ll l,ll r,ll val)
spread(rt);
ll mid=(t[rt].l+t[rt].r)>>1;
if(l<=mid)update(rt<<1,l,r,val);
if(r>mid)update(rt<<1|1,l,r,val);
t[rt].val=(t[rt<<1].val+t[rt<<1|1].val)%p;
}ll query(ll rt,ll l,ll r)
spread(rt);
ll mid=(t[rt].l+t[rt].r)>>1;
ll ans=0;
if(l<=mid)ans=(ans+query(rt<<1,l,r))%p;
if(r>mid)ans=(ans+query(rt<<1|1,l,r))%p;
return ans;
}void update1(ll x,ll y,ll z)
ll query1(ll x,ll y)
int main()
tot=0;
dep[0]=0;
dfs1(r,0);
dfs2(r,r);
build(1,1,n);
rep(i,1,m)
else if(op==2)
else if(op==3)
else if(op==4)}}
return 0;
}
P3384 模板 樹鏈剖分
傳送門 題目描述 如題,已知一棵包含n個結點的樹 連通且無環 每個節點上包含乙個數值,需要支援以下操作 操作1 格式 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z 操作2 格式 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和 操作3 格式 3 x z 表示將以x為根...
P3384 模板 樹鏈剖分
p3384 模板 樹鏈剖分 樹鏈剖分是把一棵樹劃分成幾條鏈,這幾條鏈又能組成陣列,然後把陣列建成線段樹,繼而相當於在樹樹上操作。include include include include include using namespace std const int maxn 2e5 10 int ...
P3384 模板 樹鏈剖分
題目描述 如題,已知一棵包含n個結點的樹 連通且無環 每個節點上包含乙個數值,需要支援以下操作 操作1 格式 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z 操作2 格式 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和 操作3 格式 3 x z 表示將以x為根節點的子...