众所周知,dfs序是一个处理子树问题的一个极好工具。dfs序将树形结构转化成线性结构,以快速维护树上点、链、子树等的值。虽然树链剖分的适用性更广,但对于一些卡常题,O(nlog2n)O(nlog2n)的时间复杂度就不算优秀了,所以以下的问题除了最后一道必须树剖外都用的树状数组+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(depv−depu+1)=w(depv+1)−w⋅depuw(depv−depu+1)=w(depv+1)−w⋅depu
于是分别维护w(depv+1)w(depv+1)和ww的值,点修改,区间查询即可。
#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(depu−depv+1)=w⋅depu−w(depv−1)w(depu−depv+1)=w⋅depu−w(depv−1)
于是分别维护w(depv−1)w(depv−1)和ww的值,区间修改,点查询即可。
#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;
}