形形色色的线段树练习——codevs线段树练习1-5:线段树,树状数组及分块模板
来源:互联网 发布:淘宝滑动验证码 编辑:程序博客网 时间:2024/05/18 00:38
形形色色的线段树练习
线段树练习1
http://codevs.cn/problem/1080/
//线段树版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;int t,n,m,pos,a,b;ll value;int num[10000001];int ord;struct inte{ int l,r,sum,add;}tree[10000001];/*void pushdown(int now){ if(tree[now].add) { tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1); tree[now<<1].add+=tree[now].add; tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1); tree[now<<1|1].add+=tree[now].add; tree[now].add=0;//防止再次pushdown时重复下放 }}*/void update(int now){ tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;}void build(int now,int l,int r){ tree[now].l=l; tree[now].r=r; if(l==r) { tree[now].sum=num[l]; return; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now);}void point_change(int now,int l,int r,int pos,ll value){ if(l==r) { tree[now].sum+=value; return; } int mid=(l+r)>>1; if(pos<=mid) point_change(now<<1,l,mid,pos,value); else point_change(now<<1|1,mid+1,r,pos,value); update(now);}ll ask(int now,int l,int r){ if(tree[now].l>=l&&r>=tree[now].r) { return tree[now].sum; } //pushdown(now);点修改无需标记下发 int mid=(tree[now].l+tree[now].r)>>1; ll ans=0; if(l<=mid) { ans+=ask(now<<1,l,r); } if(r>mid) { ans+=ask(now<<1|1,l,r); } return ans;}int main(){ scanf("%d",&n); for(int j=1;j<=n;j++) scanf("%d",&num[j]); build(1,1,n); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&ord); if(ord==1) { scanf("%d%d",&pos,&value); point_change(1,1,n,pos,value); } if(ord==2) { scanf("%d%d",&a,&b); printf("%lld\n",ask(1,a,b)); } } return 0;}
//树状数组版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int n,m,p,a,b,value;int num[100010],c[100010];int lowbit(int x){ return x&(-x);}void add(int k,int value){ for(int i=k;i<=n;i+=lowbit(i)) c[i]+=value;}int sum(int l,int r){ int ans=0; for(int i=r;i>0;i-=lowbit(i)) ans+=c[i]; for(int i=l-1;i>0;i-=lowbit(i)) ans-=c[i]; return ans;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); add(i,num[i]); } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&p); if(p==1) { scanf("%d%d%d",&a,&b,&value); for(int i=a;i<=b;i++) add(i,value); } if(p==2) { scanf("%d",&a); printf("%d\n",c[a]-sum(a-lowbit(a)+1,a-1)); } } return 0;}
//分块版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int n,m,a,b,p,size;int num[100010],pos[100010],sum[100010];void add(int x,int value){ num[x]+=value; sum[pos[x]]+=value;}int count(int l,int r){ int ans=0; for(int i=l;pos[i]==pos[l]&&i<=r;i++) ans+=num[i]; if(pos[l]^pos[r]) { for(int i=r;pos[i]==pos[r];i--) ans+=num[i]; } for(int i=pos[l]+1;i<pos[r];i++)//sum存储修改后区间总值 ans+=sum[i]; return ans;}int main(){ scanf("%d",&n); size=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); pos[i]=i/size; sum[pos[i]]+=num[i]; } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&p); if(p==1) { scanf("%d%d",&a,&b); add(a,b); } if(p==2) { scanf("%d%d",&a,&b); printf("%d\n",count(a,b)); } } return 0;}
线段树练习2
http://codevs.cn/problem/1081/
//线段树版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;int t,n,m,pos,a,b;ll value;int num[10000001];int ord;struct inte{ int l,r,sum,add;}tree[10000001];void pushdown(int now){ if(tree[now].add) { tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1); tree[now<<1].add+=tree[now].add; tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1); tree[now<<1|1].add+=tree[now].add; tree[now].add=0; }}void update(int now){ tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;}void build(int now,int l,int r){ tree[now].l=l; tree[now].r=r; if(l==r) { tree[now].sum=num[l]; return; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now);}void point_change(int now,int l,int r,int pos,int value){ if(l==r) { tree[now].sum+=value; return; } int mid=(l+r)>>1; if(pos<=mid) { point_change(now<<1,l,mid,pos,value); } if(pos>mid) { point_change(now<<1|1,mid+1,r,pos,value); } update(now);}void change(int now,int l,int r,ll value){ if(tree[now].l>=l&&tree[now].r<=r) { tree[now].sum+=(tree[now].r-tree[now].l+1)*value; tree[now].add+=value; return; } pushdown(now); int mid=(tree[now].l+tree[now].r)>>1; if(l<=mid) change(now<<1,l,r,value); if(r>mid) change(now<<1|1,l,r,value); update(now);}ll ask(int now,int l,int r){ if(tree[now].l>=l&&tree[now].r<=r) { return tree[now].sum; } pushdown(now); int mid=(tree[now].l+tree[now].r)>>1; ll ans=0; if(l<=mid) { ans+=ask(now<<1,l,r); } if(r>mid) { ans+=ask(now<<1|1,l,r); } return ans;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&num[i]); build(1,1,n); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&ord); if(ord==1) { scanf("%d%d%lld",&a,&b,&value); change(1,a,b,value); } if(ord==2) { scanf("%d",&a); printf("%lld\n",ask(1,a,a)); } } return 0;}
//树状数组版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int n,m,p,a,b,value;int num[100010],c[100010];int lowbit(int x){ return x&(-x);}void add(int k,int value){ for(int i=k;i<=n;i+=lowbit(i)) c[i]+=value;}int sum(int l,int r){ int ans=0; for(int i=r;i>0;i-=lowbit(i)) ans+=c[i]; for(int i=l-1;i>0;i-=lowbit(i)) ans-=c[i]; return ans;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); add(i,num[i]); } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&p); if(p==1) { scanf("%d%d%d",&a,&b,&value); for(int i=a;i<=b;i++) add(i,value); } if(p==2) { scanf("%d",&a); printf("%d\n",c[a]-sum(a-lowbit(a)+1,a-1));//左子树及自身的值之和-sum(左子树第一个元素,左子树最后一个元素) } } return 0;}
//分块版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int n,m,a,b,p,size,value;int num[100010],pos[100010],ad[100010];void add(int l,int r,int value){ for(int i=l;pos[i]==pos[l]&&i<=r;i++) num[i]+=value; if(pos[l]^pos[r]) { for(int i=r;pos[i]==pos[r];i--) num[i]+=value; } for(int i=pos[l]+1;i<pos[r];i++)//add存储区间修改量 ad[i]+=value;}int main(){ scanf("%d",&n); size=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); pos[i]=i/size; } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&p); if(p==1) { scanf("%d%d%d",&a,&b,&value); add(a,b,value); } if(p==2) { scanf("%d",&a); printf("%d\n",num[a]+ad[pos[a]]); } } return 0;}
线段树练习3
http://codevs.cn/problem/1082/
//线段树版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;int t,n,m,pos,a,b;ll value;int num[10000001];int ord;struct inte{ int l,r; ll sum,add;//注意注意 }tree[10000001];void pushdown(int now){ if(tree[now].add) { tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1); tree[now<<1].add+=tree[now].add; tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1); tree[now<<1|1].add+=tree[now].add; tree[now].add=0; }}void update(int now){ tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;}void build(int now,int l,int r){ tree[now].l=l; tree[now].r=r; if(l==r) { tree[now].sum=num[l]; return; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now);}void point_change(int now,int l,int r,int pos,int value){ if(l==r) { tree[now].sum+=value; return; } int mid=(l+r)>>1; if(pos<=mid) { point_change(now<<1,l,mid,pos,value); } if(pos>mid) { point_change(now<<1|1,mid+1,r,pos,value); } update(now);}void change(int now,int l,int r,ll value){ if(tree[now].l>=l&&tree[now].r<=r) { tree[now].sum+=(tree[now].r-tree[now].l+1)*value; tree[now].add+=value; return; } int mid=(tree[now].l+tree[now].r)>>1; pushdown(now); if(l<=mid) change(now<<1,l,r,value); if(r>mid) change(now<<1|1,l,r,value); update(now);}ll ask(int now,int l,int r){ if(tree[now].l>=l&&tree[now].r<=r) { return tree[now].sum; } pushdown(now); int mid=(tree[now].l+tree[now].r)>>1; ll ans=0; if(l<=mid) { ans+=ask(now<<1,l,r); } if(r>mid) { ans+=ask(now<<1|1,l,r); } return ans;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&num[i]); build(1,1,n); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&ord); if(ord==1) { scanf("%d%d%lld",&a,&b,&value); change(1,a,b,value); } if(ord==2) { scanf("%d%d",&a,&b); printf("%lld\n",ask(1,a,b)); } } return 0;}
//树状数组版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;ll n,m,p,a,b,value,sum1,sum2;ll num[200010],c1[200010],c2[200010];ll lowbit(ll x){ return x&(-x);}void add(ll r[],ll pos,ll value){ for(ll i=pos;i<=n;i+=lowbit(i)) r[i]+=value;}ll sigma(ll *r,ll pos){ ll ans=0; for(ll i=pos;i>0;i-=lowbit(i)) ans+=r[i]; return ans;}int main(){ scanf("%lld",&n); for(ll i=1;i<=n;i++) { scanf("%lld",&num[i]); add(c1,i,num[i]-num[i-1]); add(c2,i,(i-1)*(num[i]-num[i-1])); } scanf("%lld",&m); for(ll i=1;i<=m;i++) { scanf("%lld",&p); if(p==1) { scanf("%lld%lld%lld",&a,&b,&value); add(c1,a,value); add(c2,a,(a-1)*value); add(c1,b+1,-value); add(c2,b+1,b*(-value)); } if(p==2) { scanf("%lld%lld",&a,&b); sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1); sum2=b*sigma(c1,b)-sigma(c2,b); printf("%lld\n",sum2-sum1); } } return 0;}
这里对以上内容做一点介绍:
设原数组为num[n],差分数组c1[n],则c1[i]=num[i]-num[i-1],明显地,num[i]=sigma(c1,i),如果想要修改num[i]到num[j],只需令c1[i]+=v,c1[j+1]-=v即可.
区间修改时间复杂度O(logn)
观察式子:
num[1]+num[2]+…+num[n]
= (c1[1])+(c1[1]+c1[2])+…+(c1[1]+c1[2]+…+c1[n])
= n * c1[1]+(n-1) * c1[2]+…+c1[n]
= n * (c1[1]+c1[2]+…+c1[n]) - (0 * c1[1]+1 * c1[2]+…+(n-1) * c1[n])
维护一个数组c2[n],其中c2[i] = (i-1)*c1[i],每当修改c1的时候,就同步修改一下c2
上式
=n*sigma(c1,n) - sigma(c2,n)
区间和查询,时间复杂度O(logn)
学习借鉴自http://blog.csdn.net/fsahfgsadhsakndas/article/details/52650026十分感谢.
//分块版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll; int n,m,a,b,p,size,value;int num[200010],pos[200010];ll sum[200010],ad[200010];void add_1(int x,int value){ num[x]+=value; sum[pos[x]]+=value;}void add(int l,int r,int value){ for(int i=l;pos[i]==pos[l]&&i<=r;i++) add_1(i,value); if(pos[l]^pos[r]) { for(int i=r;pos[i]==pos[r];i--) add_1(i,value); } for(int i=pos[l]+1;i<pos[r];i++) ad[i]+=value;}ll count(int l,int r){ ll ans=0; for(int i=l;pos[i]==pos[l]&&i<=r;i++) ans+=num[i]+ad[pos[l]]; if(pos[l]^pos[r]) { for(int i=r;pos[i]==pos[r];i--) ans+=num[i]+ad[pos[r]]; } for(int i=pos[l]+1;i<pos[r];i++)//sum存储非完整修改此区间后区间总值,ad存储区间修改值 ans+=sum[i]+ad[i]*size; return ans;}int main(){ scanf("%d",&n); size=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); pos[i]=i/size; sum[pos[i]]+=num[i]; } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&p); if(p==1) { scanf("%d%d%d",&a,&b,&value); add(a,b,value); } if(p==2) { scanf("%d%d",&a,&b); printf("%lld\n",count(a,b)); } } return 0;}
后面的好像不能用树状数组做了…..
线段树练习4
http://codevs.cn/problem/4919/
//线段树版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;int n,m,a1,b1;int num[1000010],tmp[10],tnp[10];ll value;char p[10000];struct inte{ int l,r; ll add; ll c[10];}tree[1000010];void update(int now){ for(int i=0;i<7;i++) tree[now].c[i]=tree[now<<1].c[i]+tree[now<<1|1].c[i];}void pushdown(int now){ if(tree[now].add) { for(int i=0;i<7;i++) { tmp[(i+tree[now].add)%7]=tree[now<<1].c[i]; tnp[(i+tree[now].add)%7]=tree[now<<1|1].c[i]; } for(int i=0;i<7;i++) { tree[now<<1].c[i]=tmp[i]; tree[now<<1|1].c[i]=tnp[i]; } tree[now<<1].add+=tree[now].add;//注意是add+= tree[now<<1|1].add+=tree[now].add; tree[now].add=0; }}void build(int now,int l,int r){ tree[now].l=l; tree[now].r=r; if(l==r) { tree[now].c[num[l]%7]=1; return; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now);}void changes(int now,int l,int r,ll value){ if(tree[now].l>=l&&tree[now].r<=r) { int k=value%7; for(int i=0;i<7;i++) tmp[(i+k)%7]=tree[now].c[i]; for(int i=0;i<7;i++) tree[now].c[i]=tmp[i]; tree[now].add+=k; return; } int mid=(tree[now].l+tree[now].r)>>1; pushdown(now); if(l<=mid) changes(now<<1,l,r,value); if(r>mid) changes(now<<1|1,l,r,value); update(now);}ll ask(int now,int l,int r){ if(tree[now].l>=l&&tree[now].r<=r) { return tree[now].c[0]; } pushdown(now); int mid=(tree[now].l+tree[now].r)>>1; ll ans=0; if(l<=mid) { ans+=ask(now<<1,l,r); } if(r>mid) { ans+=ask(now<<1|1,l,r); } return ans;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&num[i]); build(1,1,n); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",&p); if(p[0]=='a') { scanf("%d%d%lld",&a1,&b1,&value); changes(1,a1,b1,value); } if(p[0]=='c') { scanf("%d%d",&a1,&b1); printf("%lld\n",ask(1,a1,b1)); } } return 0;}
维护区间内%7余0-6的数字的个数即可
//分块版本#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int n,m,k,size,a,b,value;int pos[100100],cnt[350][100100],num[100100],sum[1000]; char c[1000];void add_1(int x,int value){ cnt[pos[x]][num[x]]--; cnt[pos[x]][num[x]=(num[x]+value)%7]++;}void add(int l,int r,int value){ for(int i=l;pos[i]==pos[l]&&i<=r;i++) add_1(i,value); if(pos[l]^pos[r]) { for(int i=r;pos[i]==pos[r];i--) add_1(i,value); } for(int i=pos[l]+1;i<pos[r];i++) sum[i]+=value;}int count(int l,int r){ int ans=0,tmp; for(int i=l,tmp=sum[pos[l]]%7;pos[i]==pos[l]&&i<=r;i++) { if((!num[i]&&!tmp)||num[i]+tmp==7) ans++; } if(pos[l]^pos[r]) { for(int i=r,tmp=sum[pos[r]]%7;pos[i]==pos[r];i--) if((!num[i]&&!tmp)||num[i]+tmp==7) ans++; } for(int i=pos[l]+1;i<pos[r];i++) ans+=cnt[i][(7-(sum[i]%=7))^7?7-sum[i]:0]; return ans;}int main(){ scanf("%d",&n); size=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); cnt[pos[i]=i/size][num[i]%=7]++; } for(int i=n+1;i<=n+size;i++) pos[i]=i/size; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",&c); if(c[0]=='a') { scanf("%d%d%d",&a,&b,&value); add(a,b,value%7); } if(c[0]=='c') { scanf("%d%d",&a,&b); printf("%d\n",count(a,b)); } } return 0;}
线段树练习4 加强版
http://codevs.cn/problem/5037/
//这个只能用分块做了#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int n,m,k,size,a,b,value;int pos[200010+450],cnt[200100/450+10][200100],num[200100],sum[200100/450+10];//数据范围 char type;inline int read_1(){ int ret=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') { ret=ret*10+(ch-'0'); ch=getchar(); } return ret;}inline char read_2(){ char ch=getchar(); while(ch<'a'||ch>'z') ch=getchar(); return ch;}void add_1(int x,int value){ cnt[pos[x]][num[x]]--;//原来的位置数量减1 cnt[pos[x]][num[x]=(num[x]+value)%k]++;//新位置数量加1 }void add(int l,int r,int value){ for(int i=l;pos[i]==pos[l]&&i<=r;i++)//从l到从左往右第一个完整的区间 add_1(i,value); if(pos[l]^pos[r])//l与r不在同一区间内 { for(int i=r;pos[i]==pos[r];i--)//从r到从右往左第一个完整的区间 add_1(i,value); } for(int i=pos[l]+1;i<pos[r];i++)//中间完整区间修改值之和更新 sum[i]+=value;}int count(int l,int r){ int ans=0,tmp; for(int i=l,tmp=sum[pos[l]]%k;pos[i]==pos[l]&&i<=r;i++)//从l到从左往右第一个完整区间的结果统计 { if((!num[i]&&!tmp)||num[i]+tmp==k)//原本数字为k的倍数/改变后的数字为k的倍数 ans++; } if(pos[l]^pos[r]) { for(int i=r,tmp=sum[pos[r]]%k;pos[i]==pos[r];i--)//从r到从右往左第一个完整区间的结果统计 if((!num[i]&&!tmp)||num[i]+tmp==k) ans++; } for(int i=pos[l]+1;i<pos[r];i++)//中间完整区间 ans+=cnt[i][(k-(sum[i]%=k))^k?k-sum[i]:0];//若区间修改值为k的倍数则统计区间内数字中%k余0的个数,否则统计区间内数字中加上修改值为k的倍数的个数 return ans;}int main(){ n=read_1();//数据间存在空格采用手读 m=read_1(); k=read_1(); size=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); cnt[pos[i]=i/size][num[i]%=k]++; } for(int i=n+1;i<=n+size;i++) pos[i]=i/size; for(int i=1;i<=m;i++) { type=read_2(); if(type=='a') { a=read_1(); b=read_1(); value=read_1()%k; add(a,b,value); } if(type=='c') { a=read_1(); b=read_1(); printf("%d\n",count(a,b)); } } return 0;}
注释都写进程序里了.
学习借鉴自//http://blog.csdn.net/fsahfgsadhsakndas/article/details/54836031十分感谢.
线段树练习5
http://codevs.cn/problem/4927/
//线段树版本#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>using namespace std;typedef long long ll;int n,m,a,b;int num[1000010];ll value;char p[1000];struct inte{ int l,r; ll sum,add,maxn,minx,set; bool f;}tree[1000010];void pushdown(int now){ if(tree[now].f) { tree[now<<1].sum=tree[now].set*(tree[now<<1].r-tree[now<<1].l+1); tree[now<<1|1].sum=tree[now].set*(tree[now<<1|1].r-tree[now<<1|1].l+1); tree[now<<1].add=0; tree[now<<1|1].add=0; tree[now<<1].maxn=tree[now].set; tree[now<<1|1].maxn=tree[now].set; tree[now<<1].minx=tree[now].set; tree[now<<1|1].minx=tree[now].set; tree[now<<1].set=tree[now].set; tree[now<<1|1].set=tree[now].set; tree[now<<1].f=1; tree[now<<1|1].f=1; tree[now].f=0; } if(tree[now].add) { tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1); tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1); tree[now<<1].add+=tree[now].add; tree[now<<1|1].add+=tree[now].add; tree[now<<1].maxn+=tree[now].add; tree[now<<1].minx+=tree[now].add; tree[now<<1|1].maxn+=tree[now].add; tree[now<<1|1].minx+=tree[now].add; tree[now].add=0; }}void update(int now){ tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum; tree[now].maxn=max(tree[now<<1].maxn,tree[now<<1|1].maxn); tree[now].minx=min(tree[now<<1].minx,tree[now<<1|1].minx);}void build(int now,int l,int r){ tree[now].l=l; tree[now].r=r; if(l==r) { tree[now].sum=num[l]; tree[now].maxn=num[l]; tree[now].minx=num[l]; return; } int mid=(l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now);}void change(int now,int l,int r,ll value){ if(tree[now].l>=l&&tree[now].r<=r) { tree[now].sum+=value*(tree[now].r-tree[now].l+1); tree[now].add+=value; tree[now].maxn+=value; tree[now].minx+=value; return; } pushdown(now); int mid=(tree[now].l+tree[now].r)>>1; if(l<=mid) change(now<<1,l,r,value); if(r>mid) change(now<<1|1,l,r,value); update(now);}void sets(int now,int l,int r,int value){ if(tree[now].l>=l&&tree[now].r<=r) { tree[now].sum=value*(tree[now].r-tree[now].l+1); tree[now].add=0; tree[now].maxn=value; tree[now].minx=value; tree[now].set=value; tree[now].f=1; return; } pushdown(now); int mid=(tree[now].l+tree[now].r)>>1; if(l<=mid) sets(now<<1,l,r,value); if(r>mid) sets(now<<1|1,l,r,value); update(now);}ll sums(int now,int l,int r){ if(tree[now].l>=l&&tree[now].r<=r) { return tree[now].sum; } pushdown(now); ll ans=0; int mid=(tree[now].l+tree[now].r)>>1; if(l<=mid) ans+=sums(now<<1,l,r); if(r>mid) ans+=sums(now<<1|1,l,r); return ans;}ll maxs(int now,int l,int r){ if(tree[now].l>=l&&tree[now].r<=r) { return tree[now].maxn; } pushdown(now); ll ans=-1000000007; int mid=(tree[now].l+tree[now].r)>>1; if(l<=mid) ans=max(ans,maxs(now<<1,l,r)); if(r>mid) ans=max(ans,maxs(now<<1|1,l,r)); return ans;}ll mins(int now,int l,int r){ if(tree[now].l>=l&&tree[now].r<=r) { return tree[now].minx; } pushdown(now); ll ans=1000000007; int mid=(tree[now].l+tree[now].r)>>1; if(l<=mid) ans=min(ans,mins(now<<1,l,r)); if(r>mid) ans=min(ans,mins(now<<1|1,l,r)); return ans;}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&num[i]); build(1,1,n); for(int i=1;i<=m;i++) { scanf("%s",&p); if(p[0]=='a') { scanf("%d%d%lld",&a,&b,&value); change(1,a,b,value); } if(p[0]=='s'&&p[1]=='e') { scanf("%d%d%lld",&a,&b,&value); sets(1,a,b,value); } if(p[0]=='s'&&p[1]=='u') { scanf("%d%d",&a,&b); printf("%lld\n",sums(1,a,b)); } if(p[0]=='m'&&p[1]=='a') { scanf("%d%d",&a,&b); printf("%lld\n",maxs(1,a,b)); } if(p[0]=='m'&&p[1]=='i') { scanf("%d%d",&a,&b); printf("%lld\n",mins(1,a,b)); } } return 0;}
注意标记下发时set的优先级高于add
//分块版本RE'0//区间编号从0开始,第一个与最后一个区间的长度不定,O(n)预处理每个区间的左右端点#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;int n,m,k,size,a,b;ll value;int pos[100100];ll num[100100]; char c[1000];struct inte{ ll sum,add,sets,maxn,minx,ls,rs;//修改后区间和,增加值,赋值,最大值,最小值,左端点,右端点 }p[100100];void pushdown(int k)//存在set操作需标记下发 { if(p[k].sets!=-1) { for(int i=p[k].ls;i<=p[k].rs&&i<=n;i++) num[i]=p[k].sets; p[k].add=0; p[k].sets=-1; } if(p[k].add) { for(int i=p[k].ls;i<=p[k].rs&&i<=n;i++) num[i]+=p[k].add; p[k].add=0; }}void add(int l,int r,ll value){ pushdown(pos[l]); for(int i=l;pos[i]==pos[l]&&i<=r;i++) num[i]+=value; p[pos[l]].sum=0;//区间和与极值必须重新计算 p[pos[l]].maxn=-1e9+7; p[pos[l]].minx=1e9+7; for(int i=p[pos[l]].ls;i<=p[pos[l]].rs;i++)//扫描区间确定区间和与极值 { p[pos[l]].sum+=num[i]; p[pos[l]].maxn=max(p[pos[l]].maxn,num[i]); p[pos[l]].minx=min(p[pos[l]].minx,num[i]); } if(pos[l]^pos[r])//若l与r位于不同区间 { pushdown(pos[r]); for(int i=r;pos[i]==pos[r];i--) num[i]+=value; p[pos[r]].sum=0; p[pos[r]].maxn=-1e9+7; p[pos[r]].minx=1e9+7; for(int i=p[pos[r]].ls;i<=p[pos[r]].rs;i++) { p[pos[r]].sum+=num[i]; p[pos[r]].maxn=max(p[pos[r]].maxn,num[i]); p[pos[r]].minx=min(p[pos[r]].minx,num[i]); } } for(int i=pos[l]+1;i<pos[r];i++) { if(p[i].sets!=-1)//存在set标记则将value加于set上 p[i].sets+=value; else p[i].add+=value; p[i].sum+=value*size; p[i].maxn+=value; p[i].minx+=value; }}void sets(int l,int r,ll value){ pushdown(pos[l]); for(int i=l;pos[i]==pos[l]&&i<=r;i++) num[i]=value; p[pos[l]].sum=0; p[pos[l]].maxn=-1e9+7; p[pos[l]].minx=1e9+7; for(int i=p[pos[l]].ls;i<=p[pos[l]].rs;i++) { p[pos[l]].sum+=num[i]; p[pos[l]].maxn=max(p[pos[l]].maxn,num[i]); p[pos[l]].minx=min(p[pos[l]].minx,num[i]); } if(pos[l]^pos[r]) { pushdown(pos[r]); for(int i=r;pos[i]==pos[r];i--) num[i]=value; p[pos[r]].sum=0; p[pos[r]].maxn=-1e9+7; p[pos[r]].minx=1e9+7; for(int i=p[pos[r]].ls;i<=p[pos[r]].rs;i++) { p[pos[r]].sum+=num[i]; p[pos[r]].maxn=max(p[pos[r]].maxn,num[i]); p[pos[r]].minx=min(p[pos[r]].minx,num[i]); } } for(int i=pos[l]+1;i<pos[r];i++) { p[i].sum=value*size; p[i].sets=value; p[i].add=0;//将add及时清空 p[i].maxn=value; p[i].minx=value; }}ll ask_sum(int l,int r){ ll ans=0; pushdown(pos[l]); for(int i=l;pos[i]==pos[l]&&i<=r;i++) ans+=num[i]; if(pos[l]^pos[r]) { pushdown(pos[r]); for(int i=r;pos[i]==pos[r];i--) ans+=num[i]; } for(int i=pos[l]+1;i<pos[r];i++) ans+=p[i].sum; return ans;}ll ask_max(int l,int r){ ll ans=-1e9+7; pushdown(pos[l]); for(int i=l;pos[i]==pos[l]&&i<=r;i++) ans=max(ans,num[i]); if(pos[l]^pos[r]) { pushdown(pos[r]); for(int i=r;pos[i]==pos[r];i--) ans=max(ans,num[i]); } for(int i=pos[l]+1;i<pos[r];i++) ans=max(ans,p[i].maxn); return ans;}ll ask_min(int l,int r){ ll ans=1e9+7; pushdown(pos[l]); for(int i=l;pos[i]==pos[l]&&i<=r;i++) ans=min(ans,num[i]); if(pos[l]^pos[r]) { pushdown(pos[r]); for(int i=r;pos[i]==pos[r];i--) ans=min(ans,num[i]); } for(int i=pos[l]+1;i<pos[r];i++) ans=min(ans,p[i].minx); return ans;}int main(){ scanf("%d%d",&n,&m); size=sqrt(n); for(int i=0;i<=500;i++)//初始化 { p[i].maxn=-1e9+7; p[i].minx=1e9+7; p[i].sets=-1;//不可赋0,set存在清0操作,仍有风险错误(set可能存在赋-1操作,最好新开变量确定是否存在set操作,add同理) } for(int i=1;i<=n;i++) { scanf("%lld",&num[i]); pos[i]=i/size;//从0开始 p[pos[i]].sum+=num[i]; p[pos[i]].maxn=max(p[pos[i]].maxn,num[i]); p[pos[i]].minx=min(p[pos[i]].minx,num[i]); } p[pos[1]].ls=1;//处理每个区间左右端点 for(int i=2;i<=n;i++) { if(pos[i]!=pos[i-1]) { p[pos[i-1]].rs=i-1; p[pos[i]].ls=i; } } p[pos[n]].rs=n; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",&c); if(c[1]=='d') { scanf("%d%d%lld",&a,&b,&value); add(a,b,value); } if(c[1]=='e') { scanf("%d%d%lld",&a,&b,&value); sets(a,b,value); } if(c[1]=='u') { scanf("%d%d",&a,&b); printf("%lld\n",ask_sum(a,b)); } if(c[1]=='a') { scanf("%d%d",&a,&b); printf("%lld\n",ask_max(a,b)); } if(c[1]=='i') { scanf("%d%d",&a,&b); printf("%lld\n",ask_min(a,b)); } } return 0;}
//分块版本RE'1//区间编号从1开始,每个区间长度为标准的√n,可直接计算区间左右端点位置#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;int n,m,k,size,a,b;ll value;int pos[100100];ll num[100100]; char c[1000];struct inte{ ll sum,add,sets,maxn,minx,ls,rs;//修改后区间和,增加值,赋值,最大值,最小值,左端点,右端点 }p[100100];void pushdown(int k){ if(p[k].sets!=-1) { for(int i=(k-1)*size+1;i<=min(k*size,n);i++)//可直接计算位置 num[i]=p[k].sets; p[k].add=0; p[k].sets=-1; } if(p[k].add) { for(int i=(k-1)*size+1;i<=min(k*size,n);i++) num[i]+=p[k].add; p[k].add=0; }}void add(int l,int r,int value){ int k=pos[l]; pushdown(k);//下放l不完整区间 for(int i=l;i<=min(k*size,r);i++) num[i]+=value; p[k].sum=0; p[k].maxn=-1e9+7; p[k].minx=1e9+7; for(int i=(k-1)*size+1;i<=min(k*size,n);i++) { p[k].sum+=num[i]; p[k].maxn=max(p[k].maxn,num[i]); p[k].minx=min(p[k].minx,num[i]); } for(int i=pos[l]+1;i<pos[r];i++)//下放完整区间 { if(p[i].sets!=-1) p[i].sets+=value; else p[i].add+=value; p[i].sum+=value*size; p[i].maxn+=value; p[i].minx+=value; } if(pos[l]==pos[r]) return; k=pos[r]; pushdown(k);//下放r不完整区间 for(int i=(k-1)*size+1;i<=r;i++) num[i]+=value; p[k].sum=0; p[k].maxn=-1e9+7; p[k].minx=1e9+7; for(int i=(k-1)*size+1;i<=min(k*size,n);i++) { p[k].sum+=num[i]; p[k].maxn=max(p[k].maxn,num[i]); p[k].minx=min(p[k].minx,num[i]); }}void sets(int l,int r,int value){ int k=pos[l]; pushdown(k); for(int i=l;i<=min(k*size,r);i++) num[i]=value; p[k].sum=0; p[k].maxn=-1e9+7; p[k].minx=1e9+7; for(int i=(k-1)*size+1;i<=min(k*size,n);i++) { p[k].sum+=num[i]; p[k].maxn=max(p[k].maxn,num[i]); p[k].minx=min(p[k].minx,num[i]); } for(int i=pos[l]+1;i<pos[r];i++) { p[i].sets=p[i].maxn=p[i].minx=value; p[i].sum=value*size; p[i].add=0; } if(pos[l]==pos[r]) return; k=pos[r]; pushdown(k); for(int i=(k-1)*size+1;i<=r;i++) num[i]=value; p[k].sum=0; p[k].maxn=-1e9+7; p[k].minx=1e9+7; for(int i=(k-1)*size+1;i<=min(k*size,n);i++) { p[k].sum+=num[i]; p[k].maxn=max(p[k].maxn,num[i]); p[k].minx=min(p[k].minx,num[i]); }}ll ask_sum(int l,int r){ ll ans=0; int k=pos[l]; pushdown(k); for(int i=l;i<=min(k*size,r);i++) ans+=num[i]; for(int i=pos[l]+1;i<pos[r];i++) ans+=p[i].sum; if(pos[l]==pos[r]) return ans; k=pos[r]; pushdown(k); for(int i=(k-1)*size+1;i<=r;i++) ans+=num[i]; return ans;}ll ask_max(int l,int r){ ll ans=-1e9+7; int k=pos[l]; pushdown(k); for(int i=l;i<=min(k*size,r);i++) ans=max(ans,num[i]); for(int i=pos[l]+1;i<pos[r];i++) ans=max(ans,p[i].maxn); if(pos[l]==pos[r]) return ans; k=pos[r]; pushdown(k); for(int i=(k-1)*size+1;i<=r;i++) ans=max(ans,num[i]); return ans;}ll ask_min(int l,int r){ ll ans=1e9+7; int k=pos[l]; pushdown(k); for(int i=l;i<=min(k*size,r);i++) ans=min(ans,num[i]); for(int i=pos[l]+1;i<pos[r];i++) ans=min(ans,p[i].minx); if(pos[l]==pos[r]) return ans; k=pos[r]; pushdown(k); for(int i=(k-1)*size+1;i<=r;i++) ans=min(ans,num[i]); return ans;}int main(){ scanf("%d%d",&n,&m); size=sqrt(n); for(int i=1;i<=500;i++)//初始化 { p[i].maxn=-1e9+7; p[i].minx=1e9+7; p[i].sets=-1; } for(int i=1;i<=n;i++) { scanf("%lld",&num[i]); pos[i]=(i-1)/size+1; p[pos[i]].sum+=num[i]; p[pos[i]].maxn=max(p[pos[i]].maxn,num[i]); p[pos[i]].minx=min(p[pos[i]].minx,num[i]); } scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",&c); if(c[1]=='d') { scanf("%d%d%lld",&a,&b,&value); add(a,b,value); } if(c[1]=='e') { scanf("%d%d%lld",&a,&b,&value); sets(a,b,value); } if(c[1]=='u') { scanf("%d%d",&a,&b); printf("%lld\n",ask_sum(a,b)); } if(c[1]=='a') { scanf("%d%d",&a,&b); printf("%lld\n",ask_max(a,b)); } if(c[1]=='i') { scanf("%d%d",&a,&b); printf("%lld\n",ask_min(a,b)); } } return 0;}
学习借鉴自//http://www.cnblogs.com/harden/p/6486594.html
第一篇博客完成,之后我会再配图的(然而并没有).
- 形形色色的线段树练习——codevs线段树练习1-5:线段树,树状数组及分块模板
- Codevs 4927 线段树练习5(分块)
- 【CodeVS】1080 线段树练习 分块 线段树 树状数组 开放性
- Codevs 1080 线段树练习(线段树&&树状数组&&分块&&CDQ分治)
- Codevs 1081 线段树练习 2(线段树&&树状数组&&分块)
- CodeVS 1080 线段树练习 分块 块状数组
- Codevs 1082 线段树练习 3(线段树&&分块)
- codevs 1080_线段树练习_树状数组
- 【Codevs】1082 线段树练习 3 && 线段树模板
- 【数据结构】树状数组模板--CODE[VS] 1080线段树练习and1081线段树练习2
- codevs线段树练习3
- [codevs] 线段树练习4
- CODEVS 1080线段树练习
- 1080 线段树练习 codevs
- CODEVS 1080 线段树练习
- [Codevs] 1080 线段树练习
- 【codevs 1080】线段树练习
- codevs1080线段树练习(树状数组)
- JavaWeb路径问题
- vue2.0音乐app项目笔记
- 小白算法练习 树状dp POJ anniversary party
- Java智能计算
- shadertoy上手指南
- 形形色色的线段树练习——codevs线段树练习1-5:线段树,树状数组及分块模板
- struts2拦截器的实现原理及源码解析
- 【第三周】项目1-顺序表的基本运算
- MnistData的读取
- 考试总结1
- 游戏中纹理压缩格式之Texture压缩纹理特殊处理
- 大话数据结构之AVL树
- caffe的solver方法笔记
- Servlet中doGet与doPost的区别