线段树
参考资料
实现
struct SEG
{
ll a[N],val[N<<2],tag[N<<2];
void gx(int u,ll v,int len){val[u]+=v*len;tag[u]+=v;}
void push_up(int u){val[u]=val[ls]+val[rs];}
void push_down(int u,int l,int r)
{
gx(ls,tag[u],mid-l+1);
gx(rs,tag[u],r-mid);
tag[u]=0;
}
void build(int u,int l,int r)
{
if(l==r){val[u]=a[l];return;}
build(ls,l,mid);
build(rs,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,ll v)
{
if(x<=l&&r<=y){gx(u,v,r-l+1);return;}
push_down(u,l,r);
if(x<=mid)update(ls,l,mid,x,y,v);
if(y>mid)update(rs,mid+1,r,x,y,v);
push_up(u);
}
ll query(int u,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return val[u];
push_down(u,l,r);
ll res=0;
if(x<=mid)res+=query(ls,l,mid,x,y);
if(y>mid)res+=query(rs,mid+1,r,x,y);
return res;
}
};
例题
如题,已知一个数列 ,你需要进行下面两种操作:
- 将某区间每一个数加上 。
- 求出某区间每一个数的和。
Code (4)
- 三叉树
- 分块
- 树状数组
- 线段树
#include <bits/stdc++.h>
#define s1 (u*3)
#define s2 (u*3+1)
#define s3 (u*3+2)
#define m1 (l+(r-l)/3)
#define m2 (r-(r-l)/3)
using namespace std;
using ll=long long;
const int N=100005;
ll a[N],val[N<<4],tag[N<<4];
void gx(int u,ll v,int len)
{
val[u]+=v*len;
tag[u]+=v;
}
void push_up(int u)
{
val[u]=val[s1]+val[s2]+val[s3];
}
void push_down(int u,int l,int r)
{
if(!tag[u])return;
if(l<=m1)gx(s1,tag[u],m1-l+1);
if(m1<m2)gx(s2,tag[u],m2-m1);
if(r>m2)gx(s3,tag[u],r-m2);
tag[u]=0;
}
void build(int u,int l,int r)
{
if(l==r){val[u]=a[l];return;}
if(l<=m1)build(s1,l,m1);
if(m1<m2)build(s2,m1+1,m2);
if(r>m2)build(s3,m2+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,ll v)
{
if(x>r||y<l)return;
if(x<=l&&r<=y){gx(u,v,r-l+1);return;}
push_down(u,l,r);
if(l<=m1)update(s1,l,m1,x,y,v);
if(m1<m2)update(s2,m1+1,m2,x,y,v);
if(r>m2)update(s3,m2+1,r,x,y,v);
push_up(u);
}
ll query(int u,int l,int r,int x,int y)
{
if(x>r||y<l)return 0;
if(x<=l&&r<=y)return val[u];
push_down(u,l,r);
ll res=0;
if(l<=m1)res+=query(s1,l,m1,x,y);
if(m1<m2)res+=query(s2,m1+1,m2,x,y);
if(r>m2)res+=query(s3,m2+1,r,x,y);
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
build(1,1,n);
while(m--)
{
int op,x,y;
ll k;
cin>>op;
if(op==1)
{
cin>>x>>y>>k;
update(1,1,n,x,y,k);
}
else if(op==2)
{
cin>>x>>y;
cout<<query(1,1,n,x,y)<<'\n';
}
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=100005;
ll a[N],b[N],s[N];
int id[N],len;
void update(int l,int r,ll v)
{
int x=id[l],y=id[r];
if(x==y)
{
for(int i=l;i<=r;i++){a[i]+=v;s[x]+=v;}
return;
}
for(int i=l;id[i]==x;i++){a[i]+=v;s[x]+=v;}
for(int i=r;id[i]==y;i--){a[i]+=v;s[y]+=v;}
for(int i=x+1;i<y;i++){b[i]+=v;s[i]+=v*len;}
}
ll query(int l,int r)
{
int x=id[l],y=id[r];
ll res=0;
if(x==y)
{
for(int i=l;i<=r;i++)res+=a[i]+b[x];
return res;
}
for(int i=l;id[i]==x;i++)res+=a[i]+b[x];
for(int i=r;id[i]==y;i--)res+=a[i]+b[y];
for(int i=x+1;i<y;i++)res+=s[i];
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
len=sqrt(n);
for(int i=1;i<=n;i++)
{
cin>>a[i];
id[i]=(i-1)/len+1;
s[id[i]]+=a[i];
}
while(m--)
{
int op,x,y;
ll k;
cin>>op;
if(op==1)
{
cin>>x>>y>>k;
update(x,y,k);
}
else if(op==2)
{
cin>>x>>y;
cout<<query(x,y)<<'\n';
}
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=100005;
ll c1[N],c2[N];
void add(int u,ll v)
{
ll w=u*v;
while(u<N)
{
c1[u]+=v;
c2[u]+=w;
u+=u&-u;
}
}
ll sum(ll *c,int u)
{
ll res=0;
while(u)
{
res+=c[u];
u-=u&-u;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
ll x;
cin>>x;
add(i,x);
add(i+1,-x);
}
while(m--)
{
int op,x,y;
ll k;
cin>>op;
if(op==1)
{
cin>>x>>y>>k;
add(x,k);
add(y+1,-k);
}
else if(op==2)
{
cin>>x>>y;
cout<<sum(c1,y)*(y+1)-sum(c1,x-1)*x-(sum(c2,y)-sum(c2,x-1))<<'\n';
}
}
return 0;
}
#include <bits/stdc++.h>
#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
using namespace std;
using ll=long long;
const int N=100005;
ll a[N],val[N<<2],tag[N<<2];
void gx(int u,ll v,int len){val[u]+=v*len;tag[u]+=v;}
void push_up(int u){val[u]=val[ls]+val[rs];}
void push_down(int u,int l,int r)
{
gx(ls,tag[u],mid-l+1);
gx(rs,tag[u],r-mid);
tag[u]=0;
}
void build(int u,int l,int r)
{
if(l==r){val[u]=a[l];return;}
build(ls,l,mid);
build(rs,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,ll v)
{
if(x<=l&&r<=y){gx(u,v,r-l+1);return;}
push_down(u,l,r);
if(x<=mid)update(ls,l,mid,x,y,v);
if(y>mid)update(rs,mid+1,r,x,y,v);
push_up(u);
}
ll query(int u,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return val[u];
push_down(u,l,r);
ll res=0;
if(x<=mid)res+=query(ls,l,mid,x,y);
if(y>mid)res+=query(rs,mid+1,r,x,y);
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
build(1,1,n);
while(m--)
{
int op,x,y;
ll k;
cin>>op;
if(op==1)
{
cin>>x>>y>>k;
update(1,1,n,x,y,k);
}
else if(op==2)
{
cin>>x>>y;
cout<<query(1,1,n,x,y)<<'\n';
}
}
return 0;
}
如题,已知一个长度为 的数列 (),初始时 序列满足 。你需要进行下面两种操作:
- 将某区间每一个数加上 。
- 求出某区间每一个数的和。
Code (1)
#include <bits/stdc++.h>
#define mid (l+r>>1)
using namespace std;
using ull=unsigned long long;
const int N=100005;
ull val[N*120],tag[N*120];
int son[N*120][2];
int cnt=0;
void gx(int u,ull v,int len)
{
val[u]+=v*len;
tag[u]+=v;
}
void push_up(int u)
{
val[u]=(son[u][0]?val[son[u][0]]:0)+(son[u][1]?val[son[u][1]]:0);
}
void push_down(int u,int l,int r)
{
if(l==r||!tag[u])return;
if(!son[u][0])son[u][0]=++cnt;
if(!son[u][1])son[u][1]=++cnt;
gx(son[u][0],tag[u],mid-l+1);
gx(son[u][1],tag[u],r-mid);
tag[u]=0;
}
void update(int &u,int l,int r,int x,int y,ull v)
{
if(!u)u=++cnt;
if(x<=l&&r<=y){gx(u,v,r-l+1);return;}
push_down(u,l,r);
if(x<=mid)update(son[u][0],l,mid,x,y,v);
if(y>mid)update(son[u][1],mid+1,r,x,y,v);
push_up(u);
}
ull query(int u,int l,int r,int x,int y)
{
if(!u)return 0;
if(x<=l&&r<=y)return val[u];
push_down(u,l,r);
ull res=0;
if(x<=mid)res+=query(son[u][0],l,mid,x,y);
if(y>mid)res+=query(son[u][1],mid+1,r,x,y);
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,rt=0;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int op,x,y,k;
cin>>op;
if(op==1)
{
cin>>x>>y>>k;
update(rt,1,1e9,x,y,k);
}
else if(op==2)
{
cin>>x>>y;
cout<<query(rt,1,1e9,x,y)+1ll*(x+y)*(y-x+1)/2<<'\n';
}
}
return 0;
}
如题,已知一个数列 ,你需要进行下面三种操作:
- 将某区间每一个数乘上 ;
- 将某区间每一个数加上 ;
- 求出某区间每一个数的和。
Code (1)
#include <bits/stdc++.h>
#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
using namespace std;
using ll=long long;
const int N=100005;
ll a[N],val[N<<2],mul[N<<2],add[N<<2];
int mod;
void gx(int u,ll v1,ll v2,int len)
{
val[u]=(val[u]*v1+len*v2)%mod;
mul[u]=(mul[u]*v1)%mod;
add[u]=(add[u]*v1+v2)%mod;
}
void push_up(int u)
{
val[u]=(val[ls]+val[rs])%mod;
}
void push_down(int u,int l,int r)
{
gx(ls,mul[u],add[u],mid-l+1);
gx(rs,mul[u],add[u],r-mid);
mul[u]=1;
add[u]=0;
}
void build(int u,int l,int r)
{
mul[u]=1;
if(l==r){val[u]=a[l]%mod;return;}
build(ls,l,mid);
build(rs,mid+1,r);
push_up(u);
}
void update(int u,int l,int r,int x,int y,ll v1,ll v2)
{
if(x<=l&&r<=y){gx(u,v1,v2,r-l+1);return;}
push_down(u,l,r);
if(x<=mid)update(ls,l,mid,x,y,v1,v2);
if(y>mid)update(rs,mid+1,r,x,y,v1,v2);
push_up(u);
}
ll query(int u,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return val[u]%mod;
push_down(u,l,r);
ll res=0;
if(x<=mid)res=(res+query(ls,l,mid,x,y))%mod;
if(y>mid)res=(res+query(rs,mid+1,r,x,y))%mod;
return res%mod;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,q;
cin>>n>>q>>mod;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
build(1,1,n);
while(q--)
{
int op,x,y;
ll k;
cin>>op;
if(op==1)
{
cin>>x>>y>>k;
update(1,1,n,x,y,k,0);
}
else if(op==2)
{
cin>>x>>y>>k;
update(1,1,n,x,y,1,k);
}
else if(op==3)
{
cin>>x>>y;
cout<<query(1,1,n,x,y)<<'\n';
}
}
return 0;
}
小豆现在有一个数 ,初始值为 。小豆有 次操作,操作有两种类型:
1 m:将 变为 ,并输出
2 pos:将 变为 除以第 次操作所乘的数(保证第 次操作一定为类型 1,对于每一个类型 1 的操作至多会被除一次),并输出 。Code (1)
第一行包含两个正整数 ,分别表示数列中实数的个数和操作的个数。
第二行包含 个实数,其中第 个实数表示数列的第 项。
接下来 行,每行为一条操作,格式为以下三种之一:
操作 :1 x y k,表示将第 到第 项每项加上 , 为一实数。
操作 :2 x y,表示求出第 到第 项这一子数列的平均数。
操作 :3 x y,表示求出第 到第 项这一子数列的方差。Code (1)
村落里一共有 座房屋,并形成一个树状结构。然后救济粮分 次发放,每次选择两个房屋 ,然后对于 到 的路径上(含 和 )每座房子里发放一袋 类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。Code (1)
Rick 和他的同事们做出了一种新的放射性配方,与此同时很多坏人正追赶着他们。因此 Rick 想在坏人们捉到他之前把他的遗产留给 Morty。
在宇宙中一共有 个星球标号为 。Rick 现在身处于标号为 的星球(地球)但是他不知道 Morty 在哪里。
众所周知,Rick 有一个传送枪,他用这把枪可以制造出一个从他所在的星球通往其他星球(也包括自己所在的星球)的单行道路。但是由于他还在用免费版,因此这把枪的使用是有限制的。
默认情况下他不能用这把枪开启任何传送门。在网络上有 个售卖这些传送枪的使用方案。每一次你想要实施这个方案时你都可以购买它,但是每次购买后只能使用一次。每个方案的购买次数都是无限的。
网络上一共有三种方案可供购买:
- 开启一扇从星球 到星球 的传送门;
- 开启一扇从星球 到标号在 区间范围内任何一个星球的传送门。(即这扇传送门可以从一个星球出发通往多个星球);
- 开启一扇从标号在 区间范围内任何一个星球到星球 的传送门。(即这扇传送门可以从多个星球出发到达同一个星球);
Rick 并不知道 Morty 在哪儿,但是 Unity 将要通知他 Morty 的具体位置,并且他想要赶快找到通往所有星球的道路各一条并立刻出发。因此对于每一个星球(包括地球本身)他想要知道从地球到那个星球所需的最小钱数。Code (1)