形形色色的线段树练习
线段树练习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
第一篇博客完成,之后我会再配图的(然而并没有).