[dfs序基礎]系列
阿新 • • 發佈:2020-11-06
眾所周知,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;
}