HDU 6315 - Naive Operations [2018杭电多校联赛第二场 G](线段树区间更新)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6315
【题意】
给定两个长度为n的整数序列a和b,a初始值为全0,b的初始值为一个[1,n]的排列,现在有以下两种操作:
1.add L R 把序列a中[L,R]的元素全部加1
2.query L R 求区间[L,R]的所有ai/bi的和,ai/bi是整除
【输入格式】
多组输入,第一行两个整数n,q(n,q<=1e5)代表序列长度和操作次数,以下q行每行一种操作,分别为一个字符串和两个整数,即题意中的格式.
【输出格式】
对每次query操作,输出对应的查询结果
【思路】
线段树区间更新,线段树维护两个值,一个是b的最小值minb,另一个是最终答案ans,对于每个线段树的叶子结点赋初值为b[i],每次add操作时让b[i]-1,因为是整除,所以当b[i]减到0的时候最终的答案才会更新。所以在区间更新的时候,只有递归到该区间的最小值大于1或者该区间是单点的时候才停止,区间最小值大于1可以直接打lazy标记,单点可以直接将minb减1同时判断是否为0,如果该区间不是单点且最小值又刚好是1那么只能递归更新左右子结点的值。
#include<bits/stdc++.h>
using namespace std;
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
const int maxn=100050;
int n,q;
int b[maxn];
struct Tree{
int left,right;
int minb,ans;
int lazy;
}tree[maxn<<2];
void pushup(int id){
node.minb=min(lson.minb,rson.minb);
node.ans=lson.ans+rson.ans;
}
void pushdown(int id){
if(node.lazy && node.left!=node.right){
lson.lazy+=node.lazy;
rson.lazy+=node.lazy;
lson.minb-=node.lazy;
rson.minb-=node.lazy;
node.lazy=0;
}
}
void build(int id,int le,int ri){
node.left=le;
node.right=ri;
node.lazy=0;
if(le==ri){
node.minb=b[le];
node.ans=0;
return;
}
int mid=(le+ri)>>1;
build(id<<1,le,mid);
build(id<<1|1,mid+1,ri);
pushup(id);
}
void add(int id,int le,int ri){
if(node.left==le && node.right==ri && node.minb>1){
node.lazy+=1;
node.minb-=1;
return;
}
if(node.left==node.right){
node.minb-=1;
if(node.minb==0){
node.minb=b[node.left];
++node.ans;
}
return;
}
pushdown(id);
int mid=(node.left+node.right)>>1;
if(ri<=mid) add(id<<1,le,ri);
else if(le>mid) add(id<<1|1,le,ri);
else{
add(id<<1,le,mid);
add(id<<1|1,mid+1,ri);
}
pushup(id);
}
int query(int id,int le,int ri){
if(node.left==le && node.right==ri){
return node.ans;
}
pushdown(id);
int mid=(node.left+node.right)>>1;
if(ri<=mid) return query(id<<1,le,ri);
else if(le>mid) return query(id<<1|1,le,ri);
else return query(id<<1,le,mid)+query(id<<1|1,mid+1,ri);
}
int main(){
while(scanf("%d%d",&n,&q)==2){
for(int i=1;i<=n;++i) scanf("%d",&b[i]);
build(1,1,n);
while(q--){
char op[15];
int x,y;
scanf("%s%d%d",op,&x,&y);
if(op[0]=='a') add(1,x,y);
else printf("%d\n",query(1,x,y));
}
}
return 0;
}
还有另一种思路比较好想,线段树中只记录最小值和最小值的下标(如果这段区间有多个相同最小值只记录最左端的最小值下标),最终的答案存到一个BIT里,每次区间减1,直接打lazy标记就可以,然后查询对应的区间最小值是否为0,为0则根据维护的下标值把这段区间里的所有0都恢复成b[i],同时更新BIT里的最终答案
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
const int maxn=100050;
int n,q;
int b[maxn];
int bit[maxn];
struct Tree{
int left,right;
int minb,pos;
int lazy;
}tree[maxn<<2];
void add(int i,int x){
while(i<=n){
bit[i]+=x;
i+=i&-i;
}
}
int sum(int i){
int s=0;
while(i>0){
s+=bit[i];
i-=i&-i;
}
return s;
}
void pushup(int id){
if(lson.minb<=rson.minb){
node.minb=lson.minb;
node.pos=lson.pos;
}
else{
node.minb=rson.minb;
node.pos=rson.pos;
}
}
void pushdown(int id){
if(node.lazy && node.left!=node.right){
lson.lazy+=node.lazy;
rson.lazy+=node.lazy;
lson.minb-=node.lazy;
rson.minb-=node.lazy;
node.lazy=0;
}
}
void build(int id,int le,int ri){
node.left=le;
node.right=ri;
node.lazy=0;
if(le==ri){
node.minb=b[le];
node.pos=le;
return;
}
int mid=(le+ri)>>1;
build(id<<1,le,mid);
build(id<<1|1,mid+1,ri);
pushup(id);
}
void update(int id,int le,int ri,int val){
if(node.left==le && node.right==ri){
node.lazy+=val;
node.minb-=val;
return;
}
pushdown(id);
int mid=(node.left+node.right)>>1;
if(ri<=mid) update(id<<1,le,ri,val);
else if(le>mid) update(id<<1|1,le,ri,val);
else{
update(id<<1,le,mid,val);
update(id<<1|1,mid+1,ri,val);
}
pushup(id);
}
pii query(int id,int le,int ri){
if(node.left==le && node.right==ri){
return pii(node.minb,node.pos);
}
pushdown(id);
int mid=(node.left+node.right)>>1;
if(ri<=mid) return query(id<<1,le,ri);
else if(le>mid) return query(id<<1|1,le,ri);
else{
pii a=query(id<<1,le,mid);
pii b=query(id<<1|1,mid+1,ri);
if(a.first<=b.first) return a;
else return b;
}
}
int main(){
while(scanf("%d%d",&n,&q)==2){
memset(bit,0,sizeof(bit));
for(int i=1;i<=n;++i) scanf("%d",&b[i]);
build(1,1,n);
while(q--){
char op[15];
int x,y;
scanf("%s%d%d",op,&x,&y);
if(op[0]=='a'){
update(1,x,y,1);
int le=x,ri=y;
while(le<=ri){
pii tmp=query(1,le,ri);
int pos=tmp.second;
if(tmp.first==0){
update(1,pos,pos,-b[pos]);
add(pos,1);
le=pos+1;
}
else break;
}
}
else{
int ans=sum(y)-sum(x-1);
printf("%d\n",ans);
}
}
}
return 0;
}