1. 程式人生 > 實用技巧 >[dfs序基礎]系列

[dfs序基礎]系列

眾所周知,dfs序是一個處理子樹問題的一個極好工具。dfs序將樹形結構轉化成線性結構,以快速維護樹上點、鏈、子樹等的值。雖然樹鏈剖分的適用性更廣,但對於一些卡常題,\(O(nlog^2n)\)的時間複雜度就不算優秀了,所以以下的問題除了最後一道必須樹剖外都用的樹狀陣列+dfs序。這篇文章主要是為了總結一下我兩天的工程,也記錄一下從學長那裡學來的卡常技巧。

一、點修改,點查詢

??語法基礎可還行

二、點修改、子樹查詢

轉化成線性結構上的點修改,區間查詢即可。

#include<cstdio>
#include<cctype>
#define I inline
#define R register int
#define ull unsigned long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=GC;}
	n=x*w;
}
using namespace std;
const int maxn=1e6+3;
int n,a[maxn];
struct edge{int to,nxt;}ln[maxn];
int lst[maxn],tt;
I void add(int u,int v)
{
	++tt;
	ln[tt].to=v;
	ln[tt].nxt=lst[u];
	lst[u]=tt;
}
ull c[maxn],sum[maxn];
I void modify(int x,int d){for(R i=x;i<=n;i+=i&-i)c[i]+=d;}
I ull query(R l,R r)
{
	ull ans=0;
	for(;r>l;r&=r-1)ans+=c[r];
	for(;l>r;l&=l-1)ans-=c[l];
	return ans;
}
int dfn[maxn][2],ti;
void dfs(int u)
{
	sum[dfn[u][0]=++ti]=a[u];
	for(R i=lst[u];i;i=ln[i].nxt)dfs(ln[i].to);
	dfn[u][1]=ti;
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(a[i]);
	for(R i=2;i<=n;i++)
	{
		int f;
		read(f);
		add(f,i);
	}
	dfs(1);
	for(R i=1;i<=n;i++)sum[i]+=sum[i-1];
	for(R i=1;i<=n;i++)c[i]=sum[i]-sum[i&i-1];
	for(R i=1;i<=m;i++)
	{
		int op,x,y;
		read(op);
		if(op==1)
		{
			read(x);read(y);
			modify(dfn[x][0],y-a[x]);
			a[x]=y;
		}
		else
		{
			read(x);
			printf("%llu\n",query(dfn[x][0]-1,dfn[x][1]));
		}
	}
	return 0;
}

三、子樹修改,點查詢

轉換成區間修改,點查詢即可。

#include<cstdio>
#include<cctype>
#define I inline
#define R register int
#define ull unsigned long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=x*10+(ch^48);ch=GC;}
	n=x*w;
}
using namespace std;
const int maxn=1e6+3;
int n;
ull a[maxn];
struct edge{int to,nxt;}ln[maxn];
int lst[maxn],tt;
I void add(int u,int v)
{
	++tt;
	ln[tt].to=v;
	ln[tt].nxt=lst[u];
	lst[u]=tt;
}
ull c[maxn],b[maxn];
I void modify(R l,R r,ull d)
{
	for(;l<r;l+=l&-l)c[l]+=d;
	for(;r<l;r+=r&-r)c[r]-=d;
}
I ull query(R x)
{
	ull ans=0;
	for(;x;x&=x-1)ans+=c[x];
	return ans;
}
int dfn[maxn][2],ti;
void dfs(int u)
{
	b[dfn[u][0]=++ti]=a[u];
	for(R i=lst[u];i;i=ln[i].nxt)dfs(ln[i].to);
	dfn[u][1]=ti+1;
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(a[i]);
	for(R i=2;i<=n;i++)
	{
		int f;
		read(f);
		add(f,i);
	}
	dfs(1);
	for(R i=1;i<=n;i++)c[i]=b[i]-b[i&i-1];
	for(R i=1;i<=m;i++)
	{
		int op,x,y;
		read(op);
		if(op==1)
		{
			read(x);read(y);
			modify(dfn[x][0],dfn[x][1],y);
		}
		else
		{
			read(x);
			printf("%llu\n",query(dfn[x][0]));
		}
	}
	return 0;
}

四、子樹修改,子樹查詢

轉換成區間修改,區間查詢即可。

#include<cstdio>
#include<cctype>
#include<algorithm>
#define I inline
#define R register int
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=x*10+(ch^48);ch=GC;}
	n=x*w;
}
const int N=1e6+3;
int n;
ll a[N];
struct edge{int to,nxt;}ln[N];
int lst[N],tt;
I void add(int u,int v)
{
	++tt;
	ln[tt].to=v;
	ln[tt].nxt=lst[u];
	lst[u]=tt;
}
ll c[N],s[N],vc[N],vs[N];
I void modify(int l,int r,ll d)
{
	ll x=l,y=r;
	for(;l<r;l+=l&-l)c[l]+=d,s[l]+=x*d;
	l=min(l,n+1);
	for(;r<l;r+=r&-r)c[r]-=d,s[r]-=y*d;
	y-=x;
	for(;r<=n;r+=r&-r)s[r]-=y*d;
}
I ll query(int l,int r)
{
	ll ans=0,x=l+1,y=r+1;
	for(;r>l;r&=r-1)ans+=y*c[r]-s[r];
	for(;l>r;l&=l-1)ans-=x*c[l]-s[l];
	y-=x;
	for(;l;l&=l-1)ans+=y*c[l];
	return ans;
}
int dfn[N][2],ti;
void dfs(int u)
{
	dfn[u][0]=++ti;
	vc[ti]=a[u];vs[ti]=(a[u]-vc[ti-1])*ti;
	for(R i=lst[u];i;i=ln[i].nxt)dfs(ln[i].to);
	dfn[u][1]=ti;
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(a[i]);
	for(R i=2;i<=n;i++)
	{
		int f;
		read(f);
		add(f,i);
	}
	dfs(1);
	for(R i=1;i<=n;i++)c[i]=vc[i]-vc[i&i-1];
	for(R i=1;i<=n;i++)vs[i]+=vs[i-1];
	for(R i=1;i<=n;i++)s[i]=vs[i]-vs[i&i-1];
	for(R i=1;i<=m;i++)
	{
		int op,x,y;
		read(op);
		if(op==1)
		{
			read(x);read(y);
			modify(dfn[x][0],dfn[x][1]+1,y);
		}
		else
		{
			read(x);
			printf("%lld\n",query(dfn[x][0]-1,dfn[x][1]));
		}
	}
	return 0;
}

五、點修改,鏈查詢

對於每條鏈(即路徑),我們總是能夠拆成鏈兩端的點、lca及lca的父親到根的四條鏈。
於是考慮查詢點到根的路徑。注意到一個點子樹內的所有點到根的路徑中必然經過這個點,於是從上向下字首和,於是轉化成了子樹修改,點查詢

#include<cstdio>
#include<cctype>
#include<algorithm>
#define I inline
#define R register int
#define ull unsigned long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=GC;}
	n=x*w;
}
using namespace std;
const int N=1e6+3;
int n,a[N];
struct edge{int to,nxt;}ln[N];
int lst[N],tt;
I void add(int u,int v)
{
	++tt;
	ln[tt].to=v;
	ln[tt].nxt=lst[u];
	lst[u]=tt;
}
ull c[N],b[N];
I void mdf(R l,R r,ull d)
{
	for(;l<r;l+=l&-l)c[l]+=d;
	l=min(l,n+1);
	for(;r<l;r+=r&-r)c[r]-=d;
}
I ull qry(R x)
{
	ull ans=0;
	for(;x;x&=x-1)ans+=c[x];
	return ans;
}
int dfn[N][2],fa[N],dep[N],top[N],son[N],sz[N],ti;
void dfsp(int u)
{
	int mx=0;
	sz[u]=1;
	for(R i=lst[u];i;i=ln[i].nxt)
	{
		int v=ln[i].to;
		dep[v]=dep[u]+1;
		dfsp(v);
		sz[u]+=sz[v];
		if(sz[v]>mx)
		{
			mx=sz[v];
			son[u]=v;
		}
	}
}
void dfsc(int u)
{
	b[dfn[u][0]=++ti]=a[u]+b[dfn[fa[u]][0]];
	if(!top[u])top[u]=u;
	if(son[u])
	{
		top[son[u]]=top[u];
		dfsc(son[u]);
		for(R i=lst[u];i;i=ln[i].nxt)
		{
			int v=ln[i].to;
			if(!dfn[v][0])dfsc(v);
		}
	}
	dfn[u][1]=ti+1;
}
I int glca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
		else y=fa[top[y]];
	}
	return dep[x]<dep[y]?x:y;
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(a[i]);
	for(R i=2;i<=n;i++)
	{
		read(fa[i]);
		add(fa[i],i);
	}
	dfsp(1);dfsc(1);
	for(R i=1;i<=n;i++)c[i]=b[i]-b[i&i-1];
	for(R i=1;i<=m;i++)
	{
		int op,x,y;
		read(op);
		if(op==1)
		{
			read(x);read(y);
			mdf(dfn[x][0],dfn[x][1],y);
		}
		else
		{
			read(x);read(y);
			int l=glca(x,y);
			printf("%llu\n",qry(dfn[x][0])-qry(dfn[l][0])+qry(dfn[y][0])-qry(dfn[fa[l]][0]));
		}
	}
	return 0;
}

六、鏈修改,點查詢

同五,將一條路徑拆成四條鏈,從上向下差分,轉化成點修改,子樹查詢

#include<cstdio>
#include<cctype>
#include<algorithm>
#define I inline
#define R register int
#define ull unsigned long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=GC;}
	n=x*w;
}
using namespace std;
const int N=1e6+3;
int n,a[N];
struct edge{int to,nxt;}ln[N];
int lst[N],tt;
I void add(int u,int v)
{
	++tt;
	ln[tt].to=v;
	ln[tt].nxt=lst[u];
	lst[u]=tt;
}
ull c[N],b[N];
I void mdf(R x,ull d){for(;x<=n;x+=x&-x)c[x]+=d;}
I ull qry(R l,R r)
{
	ull ans=0;
	for(;r>l;r&=r-1)ans+=c[r];
	for(;l>r;l&=l-1)ans-=c[l];
	return ans;
}
int dfn[N][2],fa[N],dep[N],top[N],son[N],sz[N],ti;
void dfsp(int u)
{
	dfn[u][0]=++ti;
	ull p=a[u];
	int mx=0;
	sz[u]=1;
	for(R i=lst[u];i;i=ln[i].nxt)
	{
		int v=ln[i].to;
		dep[v]=dep[u]+1;
		p-=a[v];
		dfsp(v);
		sz[u]+=sz[v];
		if(sz[v]>mx)
		{
			mx=sz[v];
			son[u]=v;
		}
	}
	b[dfn[u][0]]=p;
	dfn[u][1]=ti;
}
void dfsc(int u)
{
	if(!top[u])top[u]=u;
	if(son[u])
	{
		top[son[u]]=top[u];
		dfsc(son[u]);
		for(R i=lst[u];i;i=ln[i].nxt)
		{
			int v=ln[i].to;
			if(v!=son[u])dfsc(v);
		}
	}
}
I int glca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
		else y=fa[top[y]];
	}
	return dep[x]<dep[y]?x:y;
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(a[i]);
	for(R i=2;i<=n;i++)
	{
		read(fa[i]);
		add(fa[i],i);
	}
	dfsp(1);dfsc(1);
	for(R i=1;i<=n;i++)b[i]+=b[i-1];
	for(R i=1;i<=n;i++)c[i]=b[i]-b[i&i-1];
	for(R i=1;i<=m;i++)
	{
		int op,a,b,d;
		read(op);
		if(op==1)
		{
			read(a);read(b);read(d);
			int l=glca(a,b);
			mdf(dfn[a][0],d);mdf(dfn[b][0],d);mdf(dfn[l][0],-d);
			if(fa[l])mdf(dfn[fa[l]][0],-d);
		}
		else
		{
			read(a);
			printf("%llu\n",qry(dfn[a][0]-1,dfn[a][1]));
		}
	}
	return 0;
}

七、鏈修改,子樹查詢

直接差分或字首和好像不好做,那我們推一下式子。
對於u的子樹中的一個點v,把v到根加一個值w,對u的貢獻為\(w(dep_v-dep_u+1)=w(dep_v+1)-w\cdot dep_u\)
於是分別維護\(w(dep_v+1)\)\(w\)的值,點修改,區間查詢即可。

#include<cstdio>
#include<cctype>
#include<algorithm>
#define I inline
#define R register int
#define ll long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=GC;}
	n=x*w;
}
using namespace std;
const int N=1e6+3;
int n;
ll in[N];
struct edge{int to,nxt;}ln[N];
int lst[N],tt;
I void add(int u,int v)
{
	++tt;
	ln[tt].to=v;
	ln[tt].nxt=lst[u];
	lst[u]=tt;
}
int dfn[N][2],fa[N],dep[N],top[N],son[N],sz[N],ti;
void dfsp(int u)
{
	dfn[u][0]=++ti;
	int mx=0;
	sz[u]=1;
	for(R i=lst[u];i;i=ln[i].nxt)
	{
		int v=ln[i].to;
		dep[v]=dep[u]+1;
		dfsp(v);
		sz[u]+=sz[v];
		in[u]+=in[v];
		if(sz[v]>mx)
		{
			mx=sz[v];
			son[u]=v;
		}
	}
	dfn[u][1]=ti;
}
void dfsc(int u)
{
	if(!top[u])top[u]=u;
	if(son[u])
	{
		top[son[u]]=top[u];
		dfsc(son[u]);
		for(R i=lst[u];i;i=ln[i].nxt)
		{
			int v=ln[i].to;
			if(v!=son[u])dfsc(v);
		}
	}
}
I int glca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
		else y=fa[top[y]];
	}
	return dep[x]<dep[y]?x:y;
}
ll c[N],s[N];
I void mdf(int x,ll d)
{
	ll t=d*(dep[x]+1);
	for(R i=dfn[x][0];i<=n;i+=i&-i)
	{
		c[i]+=d;
		s[i]+=t;
	}
}
I ll qry(int x)
{
	ll a1=0,a2=0;
	R l=dfn[x][0]-1,r=dfn[x][1];
	for(;r>l;r&=r-1)a1+=s[r],a2+=c[r];
	for(;l>r;l&=l-1)a1-=s[l],a2-=c[l];
	return a1-a2*dep[x];
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(in[i]);
	for(R i=2;i<=n;i++)
	{
		read(fa[i]);
		add(fa[i],i);
	}
	dfsp(1);dfsc(1);
	for(R i=1;i<=m;i++)
	{
		int op,a,b,d;
		read(op);
		if(op==1)
		{
			read(a);read(b);read(d);
			int l=glca(a,b);
			mdf(a,d);mdf(b,d);mdf(l,-d);
			if(fa[l])mdf(fa[l],-d);
		}
		else
		{
			read(a);
			printf("%lld\n",qry(a)+in[a]);
		}
	}
	return 0;
}

八、子樹修改,鏈查詢

對於v的子樹中的一個點u,把v的子樹加一個值w,對u的貢獻為\(w(dep_u-dep_v+1)=w\cdot dep_u-w(dep_v-1)\)
於是分別維護\(w(dep_v-1)\)\(w\)的值,區間修改,點查詢即可。

#include<cstdio>
#include<cctype>
#include<algorithm>
#define I inline
#define R register int
#define ll long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=GC;}
	n=x*w;
}
using namespace std;
const int N=1e6+3;
int n;
ll in[N];
struct edge{int to,nxt;}ln[N];
int lst[N],tt;
I void add(int u,int v)
{
	++tt;
	ln[tt].to=v;
	ln[tt].nxt=lst[u];
	lst[u]=tt;
}
int dfn[N][2],fa[N],dep[N],top[N],son[N],sz[N],ti;
void dfsp(int u)
{
	dfn[u][0]=++ti;
	int mx=0;
	sz[u]=1;
	for(R i=lst[u];i;i=ln[i].nxt)
	{
		int v=ln[i].to;
		dep[v]=dep[u]+1;
		in[v]+=in[u];
		dfsp(v);
		sz[u]+=sz[v];
		if(sz[v]>mx)
		{
			mx=sz[v];
			son[u]=v;
		}
	}
	dfn[u][1]=ti+1;
}
void dfsc(int u)
{
	if(!top[u])top[u]=u;
	if(son[u])
	{
		top[son[u]]=top[u];
		dfsc(son[u]);
		for(R i=lst[u];i;i=ln[i].nxt)
		{
			int v=ln[i].to;
			if(v!=son[u])dfsc(v);
		}
	}
}
I int glca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
		else y=fa[top[y]];
	}
	return dep[x]<dep[y]?x:y;
}
ll c[N],s[N];
I void mdf(int x,ll d)
{
	ll t=(dep[x]-1)*d;
	R l=dfn[x][0],r=dfn[x][1];
	for(;l<r;l+=l&-l)c[l]+=d,s[l]+=t;
	l=min(l,n+1);
	for(;r<l;r+=r&-r)c[r]-=d,s[r]-=t;
}
I ll qry(int x)
{
	ll a1=0,a2=0;
	for(R i=dfn[x][0];i;i&=i-1)a1+=c[i],a2+=s[i];
	return a1*dep[x]-a2;
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(in[i]);
	for(R i=2;i<=n;i++)
	{
		read(fa[i]);
		add(fa[i],i);
	}
	dfsp(1);dfsc(1);
	for(R i=1;i<=m;i++)
	{
		int op,a,b;
		read(op);
		if(op==1)
		{
			read(a);read(b);
			mdf(a,b);
		}
		else
		{
			read(a);read(b);
			int l=glca(a,b);
			printf("%lld\n",qry(a)+in[a]+qry(b)+in[b]-qry(l)-in[l]-(fa[l]?qry(fa[l])+in[fa[l]]:0));
		}
	}
	return 0;
}

九、鏈修改,鏈查詢

這道題是唯一不能用dfs序直接搞的,必須用樹鏈剖分。不過我的寫法是用的樹狀陣列,為了避免使用vector,用了一些奇技淫巧,充分利用了陣列空間。

#include<cstdio>
#include<cctype>
#include<algorithm>
#define I inline
#define R register int
#define ll unsigned long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<class T> I void read(T &n)
{
	char ch=GC;T w=1,x=0;
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=GC;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=GC;}
	n=x*w;
}
using namespace std;
const int N=1e6+3;
int n,in[N];
struct edge{int to,nxt;}e[N];
int lst[N],tt;
I void add(int u,int v)
{
	++tt;
	e[tt].to=v;
	e[tt].nxt=lst[u];
	lst[u]=tt;
}
int dfn[N],fa[N],dep[N],top[N],son[N],sz[N],ln[N],ti;
void dfsp(int u)
{
	int mx=0;
	sz[u]=1;
	for(R i=lst[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		dep[v]=dep[u]+1;
		dfsp(v);
		sz[u]+=sz[v];
		if(sz[v]>mx)
		{
			mx=sz[v];
			son[u]=v;
		}
	}
}
void dfsc(int u)
{
	dfn[u]=++ti;
	if(!top[u])top[u]=u;
	if(son[u])
	{
		top[son[u]]=top[u];
		dfsc(son[u]);
		ln[u]=ln[son[u]]+1;
		for(R i=lst[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(!dfn[v])dfsc(v);
		}
	}
	else ln[u]=1;
}
ll c[2][N];
I void mdf(int x,int y,ll d)
{
	int tp=top[x];
	ll *c0=c[0]+dfn[tp]-1,*c1=c[1]+dfn[tp]-1;
	x=dfn[x]-dfn[tp]+1;y=dfn[y]-dfn[tp]+2;
	R l=x,r=y;
	for(;l<r;l+=l&-l)c0[l]+=d,c1[l]+=d*x;
	l=min(l,ln[tp]+1);
	for(;r<l;r+=r&-r)c0[r]-=d,c1[r]-=d*y;
	x-=y;
	for(;r<=ln[tp];r+=r&-r)c1[r]+=d*x;
}
I ll qry(int x,int y)
{
	int tp=top[x];
	ll *c0=c[0]+dfn[tp]-1,*c1=c[1]+dfn[tp]-1;
	x=dfn[x]-dfn[tp];y=dfn[y]-dfn[tp]+1;
	R l=x,r=y;
	ll a0=0,a1=0,a2=0,a3=0;
	for(;r>l;r&=r-1)a1+=c0[r],a0+=c1[r];
	for(;l>r;l&=l-1)a2+=c0[l],a0-=c1[l];
	for(;l;l&=l-1)a3+=c0[l];
	return -a0+a1*(y+1)-a2*(x+1)+a3*(y-x);
}
I void trmdf(int x,int y,ll d)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		mdf(top[x],x,d);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	mdf(y,x,d);
}
I ll trqry(int x,int y)
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		ans+=qry(top[x],x);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	return ans+qry(y,x);
}
int main()
{
	int m;
	read(n);read(m);
	for(R i=1;i<=n;i++)read(in[i]);
	for(R i=2;i<=n;i++)
	{
		read(fa[i]);
		add(fa[i],i);
	}
	dfsp(1);dfsc(1);
	for(R i=1;i<=n;i++)c[0][dfn[i]]=in[i];
	for(R i=1;i<=n;i++)
	{
		if(i==top[i])
		{
			int l=ln[i];
			ll *c0=c[0]+dfn[i]-1,*c1=c[1]+dfn[i]-1;
			c1[1]=c0[1];
			for(R j=2;j<=l;j++)c1[j]=c1[j-1]+(c0[j]-c0[j-1])*j;
			for(R j=l;j>1;j--)if(j&j-1)c0[j]-=c0[j&j-1];
			for(R j=l;j>1;j--)if(j&j-1)c1[j]-=c1[j&j-1];
		}
	}
	for(R i=1;i<=m;i++)
	{
		int op,a,b,d;
		read(op);
		if(op==1)
		{
			read(a);read(b);read(d);
			trmdf(a,b,d);
		}
		else
		{
			read(a);read(b);
			printf("%lld\n",trqry(a,b));
		}
	}
	return 0;
}