1 树状数组1
problem
单点修改,区间求和。
solution
树状数组直接维护原数组。初始化的时候用changex
函数直接进行修改。
code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m,p[500005],a[500005];
int read(){
int a=0,op=1;
char c;c=getchar();
while(c<'0'||c>'9'){
if(c=='-') op=-1;c=getchar();
}
while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
return a*op;
}
int lowbit(int x){
return x&(-x);
}
void changex(int num,int x){
for(int i=num;i<=n;i+=lowbit(i)) a[i]+=x;
return ;
}
int findx(int x){
int sum=0;
for(int i=x;i;i-=lowbit(i)) sum+=a[i];
return sum;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) p[i]=read();
for(int i=1;i<=n;i++) changex(i,p[i]);
while(m--){
int b=0,x=0,y=0;
b=read(),x=read(),y=read();
if(b==1) changex(x,y);
if(b==2) printf("%d
",findx(y)-findx(x-1));
}
return 0;
}
2 树状数组2
problem
区间修改,单点查询。
solution
树状数组处理差分数组,差分数组的前缀和就是某一位的数。修改的时候,修改([x,y])其实就是第(x)位加,第(y+1)位减去。
code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m,p[500005],a[500005];
int read(){
int a=0,op=1;
char c;c=getchar();
while(c<'0'||c>'9'){
if(c=='-') op=-1;c=getchar();
}
while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
return a*op;
}
int lowbit(int x){
return x&(-x);
}
void changex(int num,int x){
for(int i=num;i<=n;i+=lowbit(i)) a[i]+=x;
return ;
}
int findx(int x){
int sum=0;
for(int i=x;i;i-=lowbit(i)) sum+=a[i];
return sum;
}
int chafen[500005];
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) p[i]=read();
chafen[1]=p[1];
for(int i=2;i<=n;i++) chafen[i]=p[i]-p[i-1];
for(int i=1;i<=n;i++) changex(i,chafen[i]);
while(m--){
int b=0,x=0,y=0,z=0;
b=read();
if(b==1)
x=read(),y=read(),z=read(),changex(x,z),changex(y+1,-z);
if(b==2) x=read(),printf("%d
",findx(x));
}
return 0;
}
3 二维树状数组1
problem
二维矩阵中,修改(A_{x,y})的值,或者查询((a,b))至((c,d))之间的矩阵元素和。
【单点修改,区间查询】
solution
与一维类似。使用二维前缀和。
code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
long long c[5005][5005];
int n,m;
long long read(){
long long a=0,op=1;
char c;c=getchar();
while(c<'0'||c>'9'){
if(c=='-') op=-1;c=getchar();
}
while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
return a*op;
}
int lowbit(int x){
return x&(-x);
}
void changex(int x,int y,long long num){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
c[i][j]+=num;
}
long long findx(int x,int y){
long long ans=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
ans+=c[i][j];
return ans;
}
void subtask1(){
int x,y,k;
x=read(),y=read(),k=read();
changex(x,y,k);
}
void subtask2(){
int x1,y1,x2,y2;
x1=read(),y1=read(),x2=read(),y2=read();
long long ans=findx(x2,y2)+findx(x1-1,y1-1)-findx(x1-1,y2)-findx(x2,y1-1);
printf("%lld
",ans);
}
signed main(){
n=read(),m=read();
//printf("%d %d
",n,m);
int op;
while(~scanf("%d",&op)){
if(op==1) subtask1();
else subtask2();
}
return 0;
}
4 二维树状数组2
problem
区间修改,单点查询。
solution
二维差分,灵魂在这里:
void insert(int a,int b,int c,int d,int k){
changex(a,b,k),changex(a,d+1,-k);
changex(c+1,b,-k),changex(c+1,d+1,k);
}
code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
long long c[5005][5005];
int n,m;
long long read(){
long long a=0,op=1;
char c;c=getchar();
while(c<'0'||c>'9'){
if(c=='-') op=-1;c=getchar();
}
while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
return a*op;
}
int lowbit(int x){
return x&(-x);
}
void changex(int x,int y,long long num){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
c[i][j]+=num;
}
long long findx(int x,int y){
long long ans=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
ans+=c[i][j];
return ans;
}
void insert(int a,int b,int c,int d,int k){
changex(a,b,k),changex(a,d+1,-k);
changex(c+1,b,-k),changex(c+1,d+1,k);
}
void subtask1(){
int a,b,c,d,k;
a=read(),b=read(),c=read(),d=read(),k=read();
insert(a,b,c,d,k);
}
void subtask2(){
int x,y;
x=read(),y=read();
printf("%lld
",findx(x,y));
}
int main(){
n=read(),m=read();
//printf("%d %d
",n,m);
int op;
while(~scanf("%d",&op)){
if(op==1) subtask1();
else subtask2();
}
return 0;
}
5 树状数组3
problem
区间修改,区间查询。
solution
考察:
[egin{align}
sum_{i=1}^na_i &= a_1+a_2+cdots +a_n \
&=d_1+(d_1+d_2)+(d_1+d_2+d_3)+cdots \
&=nd_1+(n-1)d_2+...+d_n \
&=nsum_{i=1}^nd_i -[0 imes d_1+1 imes d_2+cdots +(n-1) imes d_n)]\
end{align}]
维护差分数组(d_i)和数组((i-1)d_i),分别进行前缀和操作。区间修改借助差分数组即可(因为两个数组都满足差分的性质)
code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
long long read(){
long long a=0,op=1;
char c;c=getchar();
while(c<'0'||c>'9'){
if(c=='-') op=-1;c=getchar();
}
while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
return a*op;
}
int lowbit(int x){
return x&(-x);
}
const int maxn=1e7+10;
int n,k;
long long a[maxn],sum[maxn],c1[maxn],c2[maxn];
void addc1(int p,long long w){
while(p<=n) c1[p]+=w,p+=lowbit(p);
return ;
}
void addc2(int p,long long w){
while(p<=n) c2[p]+=w,p+=lowbit(p);
return ;
}
long long askc1(int p){
long long ans=0;
while(p) ans+=c1[p],p-=lowbit(p);
return ans;
}
long long askc2(int p){
long long ans=0;
while(p) ans+=c2[p],p-=lowbit(p);
return ans;
}
void subtask1(){
int l,r;long long x;
l=read(),r=read(),x=read();
addc1(l,x),addc1(r+1,-x),addc2(l,l*x),addc2(r+1,-(r+1)*x);
}
void subtask2(){
int l,r;l=read(),r=read();
long long ans;
ans=sum[r]+(r+1)*askc1(r)-askc2(r)-(sum[l-1]+l*askc1(l-1)-askc2(l-1));
printf("%lld
",ans);
}
int main(){
n=read(),k=read();
for(int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+a[i];
while(k--){
int a;a=read();
if(a==1) subtask1();
else subtask2();
}
return 0;
}
6 线段树1[区间gcd]
problem
给定序列,求指定区间内所有数的gcd。
solution
把线段树求和的求和回溯改成gcd。没有太大的区别。很好的板子应用。
thoughts
10pts:p<<1
写成了p<<2
100pts:正解。
code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
int read(){
int a=0,op=1;char c=getchar();
while(c>'9'||c<'0') {if(c=='-') op=-1;c=getchar();}
while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
return a*op;
}
const int maxn=2e5+10;
int a[maxn<<2],tree[maxn<<2],n,m;
int gcd(int x,int y){
return y==0?x:gcd(y,x%y);
}
void buildtree(int p,int l,int r){
if(l==r){tree[p]=a[l];return ;}
int mid=l+r>>1;
buildtree(p<<1,l,mid);buildtree(p<<1|1,mid+1,r);
tree[p]=gcd(tree[p<<1],tree[p<<1|1]);
return ;
}
int asktree(int p,int l,int r,int askl,int askr){
if(l==askl&&r==askr) return tree[p];
int mid=l+r>>1;
if(askl>mid) return asktree(p<<1|1,mid+1,r,askl,askr);
if(askr<=mid) return asktree(p<<1,l,mid,askl,askr);
else return gcd(asktree(p<<1|1,mid+1,r,mid+1,askr),asktree(p<<1,l,mid,askl,mid));
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
buildtree(1,1,n);
int v,b;
while(m--){
v=read(),b=read();
printf("%d
",asktree(1,1,n,v,b));
}
return 0;
}
7 区间(sin)和
problem
序列支持区间修改,所有的值加上k,区间查询(sumlimits_{i=l}^rsin(a_i))
solution
维护是基于
[sin(x+y)=sin xcos y+sin ycos x
]
[cos(x+y)=cos xcos y-sin xsin y
]
然后就非常好做了,我们同时维护区间 (sin)和跟区间(cos)和,上传信息可以直接相加,再维护一个加法标记,用上面的和角公式可以做到 (Theta(1))下传标记。
复杂度(Theta(mlog n))
thoughts
0pts:主函数里的(operatorname{query}(l,n,1,n,1,k))的(l)写成了(1)
code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
int read(){
int a=0,op=1;char c=getchar();
while(c>'9'||c<'0') {if(c=='-') op=-1;c=getchar();}
while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
return a*op;
}
#define mid (l+r>>1)
int n,q;
const int maxn=2e5+5;
int a[maxn];
double sink,cosk;
struct Segment_Tree{
double sine[maxn<<2],cosi[maxn<<2];
long long tag[maxn<<2];
inline void pushup(int u){
sine[u]=sine[u<<1]+sine[u<<1|1];
cosi[u]=cosi[u<<1]+cosi[u<<1|1];
}
inline void update(int u,double sinx,double cosx){
double sina=sine[u],cosa=cosi[u];
sine[u]=sina*cosx+cosa*sinx;
cosi[u]=cosa*cosx-sina*sinx;
}
inline void pushdown(int u){
if(!tag[u]) return ;
double sinx=sin(tag[u]),cosx=cos(tag[u]);
update(u<<1,sinx,cosx);update(u<<1|1,sinx,cosx);
tag[u<<1]+=tag[u],tag[u<<1|1]+=tag[u];
tag[u]=0;
}
void build(int l,int r,int u){
if(l==r){sine[u]=sin(a[l]),cosi[u]=cos(a[l]);return ;}
build(l,mid,u<<1),build(mid+1,r,u<<1|1);
pushup(u);
}
void modify(int nl,int nr,int l,int r,int u,int k){
if(nl<=l&&r<=nr){
update(u,sink,cosk);tag[u]+=k;return ;
}
pushdown(u);
if(nl<=mid) modify(nl,nr,l,mid,u<<1,k);
if(nr>mid) modify(nl,nr,mid+1,r,u<<1|1,k);
pushup(u);
}
double query(int nl,int nr,int l,int r,int u){
if(nl<=l&&r<=nr) return sine[u];
double res=0;
pushdown(u);
if(nl<=mid)res+=query(nl,nr,l,mid,u<<1);
if(nr>mid) res+=query(nl,nr,mid+1,r,u<<1|1);
return res;
}
}T;
int main(){
int op,l,r,k;
n=read();
for(int i=1;i<=n;i++) a[i]=read();
T.build(1,n,1);
q=read();
while(q--){
op=read(),l=read(),r=read();
if(op==1){
k=read();sink=sin(k),cosk=cos(k);
T.modify(l,r,1,n,1,k);
}
else printf("%.1lf
",T.query(l,r,1,n,1));
}
return 0;
}
8 线段树
problem
区间修改,区间查询。
solution
lazy标记。真没啥可写的嘿嘿嘿。
code
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e6+5;
#define int long long
int tree[maxn<<2],a[maxn],lazy[maxn<<2];
int n,m;
int read(){
int a=0,op=1;char c;c=getchar();
while(c<'0'||c>'9'){if(c=='-')op=-1;c=getchar();}
while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
return a*op;
}
void build(int p,int l,int r){
if(l==r){tree[p]=a[l];return ;}
int mid=l+r>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
tree[p]=tree[p<<1]+tree[p<<1|1];
}
void pushdown(int p,int l,int r){
int mid=l+r>>1;
tree[p<<1]+=(mid-l+1)*lazy[p];
lazy[p<<1]+=lazy[p];
tree[p<<1|1]+=(r-mid)*lazy[p];
lazy[p<<1|1]+=lazy[p];
lazy[p]=0;
}
void update(int p,int l,int r,int ll,int rr,int x){
if(l==ll&&r==rr){tree[p]+=x*(r-l+1);lazy[p]+=x;return ;}
pushdown(p,l,r);
int mid=l+r>>1;
if(ll>mid) update(p<<1|1,mid+1,r,ll,rr,x);
else if(mid>=rr) update(p<<1,l,mid,ll,rr,x);
else update(p<<1,l,mid,ll,mid,x),update(p<<1|1,mid+1,r,mid+1,rr,x);
tree[p]=tree[p<<1]+tree[p<<1|1];
}
int query(int p,int l,int r,int askl,int askr){
int mid=l+r>>1;
if(l==askl&&r==askr) return tree[p];
if(lazy[p]!=0) pushdown(p,l,r);
if(mid>=askr) return query(p<<1,l,mid,askl,askr);
else if(mid<askl) return query(p<<1|1,mid+1,r,askl,askr);
else return query(p<<1,l,mid,askl,mid)+query(p<<1|1,mid+1,r,mid+1,askr);
}
int d,f,g,h;
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
d=read();
if(d==1) f=read(),g=read(),h=read(),update(1,1,n,f,g,h);
else f=read(),g=read(),printf("%lld
",query(1,1,n,f,g));
}
return 0;
}
9 线段树[AHOI2008]
problem
区间乘法,区间加法,区间求和并取模。
solution
两个lazy标记,一个加一个乘,乘法在加法之前,pushdown是关键。
code
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e6+5;
#define int long long
int tree[maxn<<2],a[maxn],lazy1[maxn],lazy2[maxn];
struct node{
int add,mul;
}lazy[maxn<<2];
int n,m,mod;
//void pushup(int p){tree[p]=(tree[p<<1]%mod+tree[p<<1|1]%mod)%mod;}
void pushup(int x){
(tree[x]=tree[x<<1]+tree[x<<1|1])%=mod;
}
int read(){
int a=0,op=1;char c;c=getchar();
while(c<'0'||c>'9'){if(c=='-')op=-1;c=getchar();}
while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
return a*op;
}
void build(int p,int l,int r){
lazy1[p]=0,lazy2[p]=1;
if(l==r){tree[p]=a[l];return ;}
int mid=l+r>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
pushup(p);
}
void tag(int p,int l,int r,int k1,int k2){
lazy1[p]=(lazy1[p]*k2+k1)%mod;
lazy2[p]=(lazy2[p]*k2)%mod;
tree[p]=(tree[p]*k2+k1*(r-l+1))%mod;
}
void pushdown(int p,int l,int r){
int mid=l+r>>1;
tag(p<<1,l,mid,lazy1[p],lazy2[p]);
tag(p<<1|1,mid+1,r,lazy1[p],lazy2[p]);
lazy1[p]=0,lazy2[p]=1;
}
void modify1(int p,int l,int r,int nl,int nr,int k){
if(nl<=l&&r<=nr) {tag(p,l,r,0,k);return ;}
int mid=l+r>>1;
pushdown(p,l,r);
if(nl<=mid) modify1(p<<1,l,mid,nl,nr,k);
if(nr>mid) modify1(p<<1|1,mid+1,r,nl,nr,k);
pushup(p);
}
void modify2(int p,int l,int r,int nl,int nr,int k){
if(nl<=l&&r<=nr) {tag(p,l,r,k,1);return ;}
int mid=l+r>>1;
pushdown(p,l,r);
if(nl<=mid) modify2(p<<1,l,mid,nl,nr,k);
if(nr>mid) modify2(p<<1|1,mid+1,r,nl,nr,k);
pushup(p);
}int x,y,k;
int query(int p,int l,int r,int nl,int nr){
int res=0;
if (nl<=l&&r<=nr) return tree[p];
int mid=l+r>>1;
pushdown(p,l,r);
if(nl<=mid) res+=query(p<<1,l,mid,nl,nr);
if(nr>mid) res+=query(p<<1|1,mid+1,r,nl,nr);
return res%mod;
}
void d(){
printf("%lld %lld %lld:",x,y,k);
for(int i=1;i<=n;i++) printf("%lld ",query(1,1,n,i,i));
printf("
");
}
signed main(){
n=read(),mod=read();
//printf("%lld
",mod);
for(int i=1;i<=n;i++) a[i]=read();
m=read();
build(1,1,n);
for(int i=1;i<=m;i++) {
int op=read();
if(op==1) x=read(),y=read(),k=read(),modify1(1,1,n,x,y,k);
if(op==2) x=read(),y=read(),k=read(),modify2(1,1,n,x,y,k);
if(op==3) x=read(),y=read(),printf("%lld
",(query(1,1,n,x,y)%mod));
}
return 0;
}
10 线段树
problem
区间修改,区间平均数,区间方差。
solution
code
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
int read(){
int a=0,op=1;char c;c=getchar();
while(c<'0'||c>'9'){if(c=='-')op=-1;c=getchar();}
while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
return a*op;
}
int n,m;
double a[maxn],tree1[maxn<<2],tree2[maxn<<2];
double lazy[maxn<<2];
void pushup(int p){
tree1[p]=tree1[p<<1]+tree1[p<<1|1];
tree2[p]=tree2[p<<1]+tree2[p<<1|1];
}
void build(int p,int l,int r){
if(l==r) {tree1[p]=a[l],tree2[p]=a[l]*a[l];return ;}
int mid=l+r>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
pushup(p);
}
void tag(int p,int l,int r,double k){
lazy[p]+=k;
tree2[p]+=(r-l+1)*k*k+tree1[p]*2*k;
tree1[p]+=(r-l+1)*k;
}
void pushdown(int p,int l,int r){
int mid=l+r>>1;
tag(p<<1,l,mid,lazy[p]);
tag(p<<1|1,mid+1,r,lazy[p]);
lazy[p]=0;
}
void modify(int p,int l,int r,int nl,int nr,double k){
if(nl<=l&&r<=nr){tag(p,l,r,k);return ;}
int mid=l+r>>1;
pushdown(p,l,r);
if(nl<=mid) modify(p<<1,l,mid,nl,nr,k);
if(nr>mid) modify(p<<1|1,mid+1,r,nl,nr,k);
pushup(p);
}
double query1(int p,int l,int r,int nl,int nr){
double res=0;
if(nl<=l&&r<=nr) return tree1[p];
int mid=l+r>>1;
pushdown(p,l,r);
if(nl<=mid) res+=query1(p<<1,l,mid,nl,nr);
if(nr>mid) res+=query1(p<<1|1,mid+1,r,nl,nr);
return res;
}
double query2(int p,int l,int r,int nl,int nr){
double res=0;
if(nl<=l&&r<=nr) return tree2[p];
int mid=l+r>>1;
pushdown(p,l,r);
if(nl<=mid) res+=query2(p<<1,l,mid,nl,nr);
if(nr>mid) res+=query2(p<<1|1,mid+1,r,nl,nr);
return res;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) scanf("%lf",&a[i]);
build(1,1,n);
for(int i=1;i<=m;i++){
int op,x,y;double k;
op=read();
if(op==1){
x=read(),y=read();
scanf("%lf",&k);
modify(1,1,n,x,y,k);
}
if(op==2){
x=read(),y=read();
double tmp=query1(1,1,n,x,y);
double ans=tmp/((y-x+1)*1.0);
printf("%.4lf
",ans);
}
if(op==3){
x=read(),y=read();
double tmp1=query1(1,1,n,x,y),tmp2=query2(1,1,n,x,y);
double ans1=tmp2/((y-x+1)*1.0),ans2=tmp1/((y-x+1)*1.0);
ans2=ans2*ans2;
double ans=ans1-ans2;
printf("%.4lf
",ans);
}
}
return 0;
}