线段树的模板
//这个是区间的最大值。
#define maxn 1000005
#define mod 1000000007
int a[maxn];
struct node
{
int l,r,ma;
}tree[maxn*4];
void build(int l,int r,int p) //表示当前节点的标号,也就是根节点的编号。
{
tree[p].l = l;
tree[p].r = r;
tree[p].ma = -1;
if(l == r)
{
tree[p].ma = a[l];
return;
}
int mid = (l+r)/2;
build(l,mid,p*2);
build(mid+1,r,p*2+1);
tree[p].ma = max(tree[p*2].ma,tree[p*2+1].ma);
}
int query(int x,int y,int p)
{
if(x == tree[p].l && y == tree[p].r)
return tree[p].ma;
int mid = (tree[p].l + tree[p].r)/2;
if(x > mid)
return query(x,y,p*2+1);
else if(y <= mid)
return query(x,y,p*2);
else
return max(query(x,mid,p*2),query(mid+1,y,p*2+1));
}
题目意思:
给定n个数,与一个数m,求ai右边最后一个至少比ai大m的数与这个数之间有多少个数
思路:
对于每一个数,利用二分的方法求他右边大于等于ai+m的数的最后一个值。
总是先求右半段
关键在于怎么二分呢?
利用线段树存储区间最大值,看这个区间的最大值是不是比ai+m大
代码:
using namespace std;
#define maxn 1000005
#define mod 1000000007
int a[maxn];
struct node
{
int l,r,ma;
}tree[maxn*4];
void build(int l,int r,int p)
{
tree[p].l = l;
tree[p].r = r;
tree[p].ma = -1;
if(l == r)
{
tree[p].ma = a[l];
return;
}
int mid = (l+r)/2;
build(l,mid,p*2);
build(mid+1,r,p*2+1);
tree[p].ma = max(tree[p*2].ma,tree[p*2+1].ma);
}
int query(int x,int y,int p)
{
if(x == tree[p].l && y == tree[p].r)
return tree[p].ma;
int mid = (tree[p].l + tree[p].r)/2;
if(x > mid)
return query(x,y,p*2+1);
else if(y <= mid)
return query(x,y,p*2);
else
return max(query(x,mid,p*2),query(mid+1,y,p*2+1));
}
int main()
{
int T,i,j,k,n,q,x,y,m,s,l,r,mid;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,n,1);
for(i=1;i<n;i++)
{
s=m+a[i];
l=i+1;
r=n;
while(l<r)
{
mid=(l+r+1)/2;
if(query(mid,r,1)>=s)//先找右半段:
{
l=mid;
}
else if(query(l,mid,1)>=s)
{
r=mid-1;
}
else
{
break;
}
}
if(a[l]>=s)
cout<<l-i-1<<' ';
else
cout<<"-1"<<' ';
}
cout<<"-1"<<endl;
return 0;
}
在上面的例子中只涉及到查询,下面的例子包含修改和区间查询:
题目:
给定一数列,规定有两种操作,一是修改某个元素,二是求区间的连续和。
输入
输入数据第一行包含两个正整数n,m(n<=100000,m<=500000),以下是m行,
输出
每行有三个正整数k,a,b(k=0或1, a,b<=n).k=0时表示将a处数字加上b,k=1时表示询问区间[a,b]内所有数的和。对于每个询问输出对应的答案。
这题是个模板题,只要注意节点初始值都为0。
#include<iostream>
using namespace std;
const int maxn=1e5+10;
#define ll long long
ll sum[maxn*4];//要开四倍空间
ll query(int x,int y,int l,int r,int rt)
{
if(x<=l&&y>=r)//如果当前的区间被要查询的区间包含,sum就直接取
return sum[rt];
ll ans=0;
int mid=(l+r)/2;
if(x<=mid)
ans+=query(x,y,l,mid,2*rt);
if(y>mid)
ans+=query(x,y,mid+1,r,2*rt+1);
return ans;
}
void update(int x,int y,int l,int r,int rt)
{
if(l==r)
{
sum[rt]+=y;
return;
}
int mid=(l+r)/2;
if(x<=mid)
update(x,y,l,mid,2*rt);
else
update(x,y,mid+1,r,2*rt+1);
sum[rt]=sum[2*rt]+sum[2*rt+1];
}
void build(int l,int r,int rt)//编号rt节点包含的区间是[l,r]
{
sum[rt]=0;//初始化为0
if(l==r)
return ;
int mid=(l+r)/2;
build(l,mid,2*rt);
build(mid+1,r,2*rt+1);
sum[rt]=sum[2*rt]+sum[2*rt+1];
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
build(1,n,1);
while(m--)
{
int k,a,b;
scanf("%d%d%d",&k,&a,&b);
if(k==1)
{
printf("%lld\n",query(a,b,1,n,1));
}
else if(k==0)
{
update(a,b,1,n,1);
}
}
return 0;
}