题意:
给你n个数,q个询问,这n个数b[i]是不变的,还有n个数a[i]初始值为0,询问有两种,一种是在(l,r)的区间内,ai加1,另一种查询(l,r)内a[i]/b[i]的值。
思路:
比赛的时候并没有什么思路,因为我们队对线段树不太熟悉,赛后看了大佬的博客才知道原来可以用线段树维护三个值,一个是a[i]的区间最大值maxa,一个是b[i]的区间最小值minb,一个是答案cnt。当a[i]的区间最大值大于b[i]的区间最小值,我们就需要找到那个位置,并把那个位置上的cnt++,minb+=b[i]。知道思路后就是一个很裸的线段树了。
至于怎么找到那个引起a[i]的区间最大值大于b[i]的区间最小值的位置,有人说要二分找那个位置,其实不需要的,我们可以在区间更新的时候,直接判断是否是这个位置的maxa>minb,如果是直接更新。因为区间更新肯定可以遍历所有区间内的位置,所以不可能会漏掉。
代码:
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<string>
#include<math.h>
#include<algorithm>
#include<functional>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<unordered_map>
#define INF 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
using namespace std;
#define MAXN 100010
struct node {
int l, r;//区间端点
int add;//延迟标记
int maxa,minb,cnt;
}tree[MAXN * 4];//必须乘以4
int b[MAXN];
void push_up(int rt)
{
tree[rt].minb=min(tree[rt<<1].minb,tree[rt<<1|1].minb);
tree[rt].cnt=tree[rt<<1].cnt+tree[rt<<1|1].cnt;
tree[rt].maxa=max(tree[rt<<1].maxa,tree[rt<<1|1].maxa);
}
void build(int p, int l, int r)//调用时build(1,l,r);
{
tree[p].add=0;
tree[p].l = l, tree[p].r = r;
if (l == r){
tree[p].cnt=tree[p].maxa=0;
tree[p].minb=b[l];
return;
}
int mid = (l + r) >>1;
build(p <<1, l, mid);
build(p <<1|1, mid + 1, r);
push_up(p);
}
void spread(int p){
if (tree[p].add){
int v=tree[p].add;
tree[p].add = 0;//记得清零
tree[p<<1].maxa+=v;
tree[p<<1|1].maxa+=v;
tree[p<<1].add+=v;
tree[p<<1|1].add+=v;
}
}
void change(int p, int l, int r, int d)//区间更新,调用时change(1,l,r,d)
{
if (l <= tree[p].l&&r >= tree[p].r)//若当前节点的区间在l,r内部
{
tree[p].maxa++;
if(tree[p].maxa<tree[p].minb)
{
tree[p].add++;
return ;
}
if(tree[p].l==tree[p].r&&tree[p].maxa>=tree[p].minb)//这个位置被找到了
{
tree[p].cnt++;
tree[p].minb+=b[tree[p].l];
return ;
}
}
spread(p);//向下一层拓展延迟标记
int mid = (tree[p].l + tree[p].r) / 2;
if (l <= mid)change(p <<1, l, r, d);
if (r > mid)change(p <<1|1, l, r, d);
push_up(p);
}
int ask(int p, int l, int r)//调用ask(1,l,r)
{
if (l <= tree[p].l&&r >= tree[p].r)
return tree[p].cnt;
spread(p);//向下一层拓展延迟标记
int mid = (tree[p].l + tree[p].r) >>1;
int ans=0;
if (l <= mid)ans+= ask(p <<1, l, r);
if (r > mid) ans+= ask(p <<1|1, l, r);
return ans;
}
int main()
{
int n,q;
while(scanf("%d%d",&n,&q)!=EOF){
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
build(1,1,n);
while(q--)
{
char a[10];
scanf("%s",a);
int l,r;
scanf("%d%d",&l,&r);
if(a[0]=='a')
change(1,l,r,1);
else if(a[0]=='q')
{
int ans=ask(1,l,r);
printf("%d\n",ans);
}
}
}
return 0;
}