HDU 6315 Naive Operations 暴力,线段树

题意:给出长度为n的排列b,初始全部为0的序列a.
操作1:将a[L:R]的元素 + 1.
操作2:询问SUM(a[i]/b[i]) i=[L:R].
n,Q<=1e5.

因为b是排列并且每次操作都是将一段区间的值加1, 那么SUM(a[i]/b[[i])的答案不会超过nlogn.(q/1+q/2+q/....q/n.)

基本思路:令t[i]为还需要加多少 才能使val=(a[i]/b[i])的值增加1.
那么每次区间加操作 变为t[i]的区间减. 
当t[i]为0时 需要将t[i]复原为b[i],并将val=a[i]/b[i]的值加1.
如何知道t[i]被减为0? 线段树维护t[i]最小值和区间val的和.
因为t[i]的复原操作不会超过nlogn 每次复原都走到叶子 这一部分复杂度为O(nlog^2n).最小值和区间和操作复杂度为O(nlogn).

#include <bits/stdc++.h>
#define ls (o<<1)
#define rs ((o<<1)|1)
using namespace std;
const int N=1e5+5;
int n,Q,a[N],b[N];
char op[20];
struct node{
	int l,r,sum,mn,laz;
}t[N<<2];
void push_up(int o){
	t[o].mn=min(t[ls].mn,t[rs].mn);
	t[o].sum=t[ls].sum+t[rs].sum;
}
void build(int o,int l,int r){
	t[o].l=l,t[o].r=r;
	t[o].laz=t[o].sum=0;
	if(l==r){
		t[o].mn=b[l];
		return;
	}
	int m=l+r>>1;
	build(ls,l,m);
	build(rs,m+1,r);
	push_up(o);
}
void push_down(int o){
	if(t[o].laz==0)	return;
	t[ls].laz+=t[o].laz;
	t[rs].laz+=t[o].laz;
	t[ls].mn-=t[o].laz;
	t[rs].mn-=t[o].laz;
	t[o].laz=0;
}
void update(int o,int ql,int qr){
	int l=t[o].l,r=t[o].r;
	if(t[o].mn>1&&ql<=l&&qr>=r){
		t[o].laz++;
		t[o].mn--;
		return;
	}
	if(l==r&&t[o].mn==1){
		t[o].sum++;
		t[o].laz=0;
		t[o].mn=b[l];
		return;
	}
	push_down(o);
	int mid=l+r>>1;
	if(ql<=mid)	update(ls,ql,qr);
	if(qr>mid)	update(rs,ql,qr);
	push_up(o);
}
int query(int o,int ql,int qr){
	int l=t[o].l,r=t[o].r;
	if(ql<=l&&qr>=r)	return t[o].sum;
	int mid=l+r>>1,res=0;
	if(ql<=mid)	res+=query(ls,ql,qr);
	if(qr>mid)	res+=query(rs,ql,qr);
	return res;
}
int main(){
	int l,r;
	while(~scanf("%d%d",&n,&Q)){
		for(int i=1;i<=n;i++)	scanf("%d",&b[i]);
		build(1,1,n);
		while(Q--){
			scanf("%s%d%d",op,&l,&r);
			if(op[0]=='a')	update(1,l,r);	
			else	printf("%d\n",query(1,l,r));
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值