树链剖分
对于树上路径上点权,边权问题,查询次数很多,如果用差分的话,每次查询前都有对c数组求一遍树上前缀和,然后dfs求个dis才能知道两点之间信息,这样的话对于修改然后查询,修改然后查询。。。。这样的复杂度就很高了,于是就要用树链剖分。
数链剖分每次查询和修改的时间复杂度都是(logn)^2,
下面贴几篇写得比较纤细的博客
https://www.cnblogs.com/chinhhh/p/7965433.html
https://www.luogu.org/problemnew/solution/P3384?page=2
#include<bits/stdc++.h>
using namespace std;
#define ls rt<<1
#define rs (rt<<1)+1
#define sjs srand(time(NULL));
#define fuck(x) cout<<#x<<" "<<x<<endl;
const int maxn=1e5+10;
struct edge
{
int v,nxt;
edge(int v=0,int nxt=0){
this->v=v;
this->nxt=nxt;
}
}e[maxn<<1];
int n,m,r,mod,tim,nod[maxn],head[maxn],sz[maxn],son[maxn],fa[maxn],dep[maxn],top[maxn],id[maxn],rk[maxn],sum[maxn<<2],lazy[maxn<<2];//son[i]表示重儿子,top[i]表示 1.i节点连了重链那就是所在重链的顶端节点,2.如果没连的话那就是它本身,id[i]表示i节点的dfs序,rk[i]是映射
void dfs1(int now,int f)
{
sz[now]=1;
fa[now]=f;
dep[now]=dep[f]+1;
for(int i=head[now];i;i=e[i].nxt)
{
int v=e[i].v;
if(v==f) continue;
dfs1(v,now);
sz[now]+=sz[v];
if(sz[v]>sz[son[now]])
son[now]=v;
}
}
void dfs2(int now,int t){
top[now]=t;
id[now]=++tim;
rk[tim]=now;
if(!son[now])
return ;
dfs2(son[now],t);
for(int i=head[now];i;i=e[i].nxt)
{
int v=e[i].v;
if(v!=son[now]&&v!=fa[now])
dfs2(v,v);
}
}
void pushup(int rt)
{
sum[rt]=(sum[ls]+sum[rs])%mod;
}
void pushdown(int rt,int L,int R)
{
if(lazy[rt])
{
int mid=(L+R)>>1;
lazy[ls]+=lazy[rt];
lazy[ls]%=mod;
lazy[rs]+=lazy[rt];
lazy[rs]%=mod;
sum[ls]+=(mid-L+1)%mod*lazy[rt];
sum[ls]%=mod;
sum[rs]+=(R-mid)%mod*lazy[rt];
sum[rs]%=mod;
lazy[rt]=0;
}
}
void build(int rt,int L,int R){
if(L==R)
{
sum[rt]=nod[rk[L]]%mod;
return ;
}
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
pushup(rt);
}
void update(int rt,int L,int R,int l,int r,int k)
{
if(l<=L&&r>=R)
{
lazy[rt]+=k;
lazy[rt]%=mod;
sum[rt]+=(R-L+1)%mod*k;
sum[rt]%=mod;
return ;
}
pushdown(rt,L,R);
int mid=(L+R)>>1;
if(r<=mid)
update(ls,L,mid,l,r,k);
else
if(l>=mid+1)
update(rs,mid+1,R,l,r,k);
else
{
update(ls,L,mid,l,r,k);
update(rs,mid+1,R,l,r,k);
}
pushup(rt);
}
int query(int rt,int L,int R,int l,int r)
{
if(l<=L&&r>=R)
return sum[rt];
pushdown(rt,L,R);
int mid=(L+R)>>1;
if(r<=mid)
return query(ls,L,mid,l,r);
else
if(l>=mid+1)
return query(rs,mid+1,R,l,r);
else
{
int ans=0;
ans+=query(ls,L,mid,l,r);
ans%=mod;
ans+=query(rs,mid+1,R,l,r);
ans%=mod;
return ans;
}
}
void updrange(int x,int y,int k)
{
int fx=top[x],fy=top[y];
k%=mod;
while(fx!=fy){
if(dep[fx]>=dep[fy]){
update(1,1,n,id[fx],id[x],k);
x=fa[fx],fx=top[x];
}
else
{
update(1,1,n,id[fy],id[y],k);
y=fa[fy],fy=top[y];
}
}
if(id[x]<=id[y])
update(1,1,n,id[x],id[y],k);
else
update(1,1,n,id[y],id[x],k);
}
int querange(int x,int y){
int ans=0,fx=top[x],fy=top[y];
while(fx!=fy){
if(dep[fx]>=dep[fy]){
ans+=query(1,1,n,id[fx],id[x]);
ans%=mod;
x=fa[fx],fx=top[x];
}
else{
ans+=query(1,1,n,id[fy],id[y]);
ans%=mod;
y=fa[fy],fy=top[y];
}
}
if(id[x]<=id[y])
ans+=query(1,1,n,id[x],id[y]);
else
ans+=query(1,1,n,id[y],id[x]);
ans%=mod;
return ans;
}
void updsontree(int x,int y)
{
update(1,1,n,id[x],id[x]+sz[x]-1,y);
}
int quesontree(int x)
{
return query(1,1,n,id[x],id[x]+sz[x]-1);
}
int main(){
int cnt=0;
scanf("%d%d%d%d",&n,&m,&r,&mod);
for(int i=1;i<=n;i++) scanf("%d",&nod[i]);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
e[++cnt]=edge(v,head[u]);
head[u]=cnt;
e[++cnt]=edge(u,head[v]);
head[v]=cnt;
}
dfs1(r,0);
dfs2(r,r);
build(1,1,n);
while(m--){
int op,x,y,z;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d",&x,&y,&z);
updrange(x,y,z);
}
else
if(op==2)
{
scanf("%d%d",&x,&y);
printf("%d\n",querange(x,y));
}
else
if(op==3){
scanf("%d%d",&x,&y);
updsontree(x,y);
}
else
{
scanf("%d",&x);
printf("%d\n",quesontree(x));
}
}
return 0;
}