HDU 6315 Naive Operations 【线段树 + 区间运算】

2018 Multi-University Training Contest 2 1007

Naive Operations

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 502768/502768 K (Java/Others)
Total Submission(s): 1103    Accepted Submission(s): 450

Problem Description

In a galaxy far, far away, there are two integer sequence a and b of length n.
b is a static permutation of 1 to n. Initially a is filled with zeroes.
There are two kind of operations:
1. add l r: add one for al,al+1...ar
2. query l r: query ∑ri=l⌊ai/bi⌋

Input

There are multiple test cases, please read till the end of input file.
For each test case, in the first line, two integers n,q, representing the length of a,b and the number of queries.
In the second line, n integers separated by spaces, representing permutation b.
In the following q lines, each line is either in the form 'add l r' or 'query l r', representing an operation.
1≤n,q≤100000, 1≤l≤r≤n, there're no more than 5 test cases.

Output

Output the answer for each 'query', each one line.

Sample Input

5 12

1 5 2 4 3

add 1 4

query 1 4

add 2 5

query 2 5

add 3 5

query 1 5

add 2 4

query 1 4

add 2 5

query 2 5

add 2 2

query 1 5

Sample Output

1

1

2

4

4

6

Source

2018 Multi-University Training Contest 2


题意:有一个数组a[] 初始值全为0,还有一个静态数组b[],里面的值是 1 - n 不重复的

对数组a做线段树的操作,唯一不同的是查询操作

它在查询的时候要求 区间 [l,r] 里所有值 / b[i] 的值 (整数相除,不带小数)

思路:

首先题目的关键是 b数组是 1-n不重复的,那么也就是说,我对 a 数组做了再多的操作,能够使得最后 a[i] / b[i] 的值发生改变的并不多,因为 n 最大是 100000,所以 b[i] 最大是 100000,而我操作次数最多也就是 100000,所以极限情况下,数组长度为 100000,我做100000次更新操作,每次都更新 [1,100000]的区间,那么 [50001,100000] 区间里的  a[i] / b[i] 结果才只会被改变一次,这是一个 logn 级别的更新频率,所以我们要维护的就是 a[i] / b[i] 的值,且叶子节点的更新次数并不会特别多,所以时间复杂度是足够的。

但是对于线段树而言,维护的值可以有多个,那么我们维护一个区间被更新的最大次数 maxA,和使得 a[i] / b[i] 的值发生改变所需要花费更新次数的最小值 minB,那么当我们更新到了叶子节点的时候,如果 maxA > minB,那就意味着 a[i] / b[i] 发生了变化,我们用 sum 记录 a[i] / b[i] 的值,这个时候 sum ++ ,然后我们让  minB += b[i] ,这样就能判断下次的 a[i] / b[i] 发生变化

用 lazy 懒惰更新 minA,区间维护 maxA,minB,sum,最后查询的时候查询 sum 即可

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define mem(a,x) memset(a,x,sizeof(a))

const int maxn = 100005;

struct node{
	int l,r,maxA,minB,sum,lazy;
}tree[maxn * 4];

int b[maxn];

void push_up(int rt){
	tree[rt].minB = min(tree[rt << 1].minB,tree[rt << 1 | 1].minB);
	tree[rt].maxA = max(tree[rt << 1].maxA,tree[rt << 1 | 1].maxA);
	tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}

void push_down(int rt){
	if(tree[rt].lazy){
		tree[rt << 1].lazy += tree[rt].lazy;
		tree[rt << 1 | 1].lazy += tree[rt].lazy;
		tree[rt << 1].maxA += tree[rt].lazy;
		tree[rt << 1 | 1].maxA += tree[rt].lazy;
		tree[rt].lazy = 0;
	}
}

void build_tree(int l,int r,int rt){
	tree[rt].l = l;
	tree[rt].r = r;
	tree[rt].lazy = 0;
	if(l == r){
		tree[rt].minB = b[l];
		tree[rt].sum = 0;
		tree[rt].maxA = 0;
		return;
	}	
	int mid = (l + r) >> 1;
	build_tree(l,mid,rt << 1);
	build_tree(mid + 1,r,rt << 1 | 1);
	push_up(rt);
}

void update_interval(int l,int r,int rt){
	if(l <= tree[rt].l && r >= tree[rt].r){
		tree[rt].maxA++;
		if(tree[rt].maxA < tree[rt].minB){
			tree[rt].lazy++;
			return;
		}
		if(tree[rt].l == tree[rt].r && tree[rt].maxA >= tree[rt].minB){
			tree[rt].sum++;
			tree[rt].minB += b[tree[rt].l];
			return;
		}
	}
	push_down(rt);
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	if(l <= mid){
		update_interval(l,r,rt << 1);
	}
	if(r > mid){
		update_interval(l,r,rt << 1 | 1);
	}
	push_up(rt);
}

int query_interval(int l,int r,int rt){
	if(l <= tree[rt].l && r >= tree[rt].r)
		return tree[rt].sum;
	push_down(rt);
	int ans = 0;
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	if(l <= mid){
		ans += query_interval(l,r,rt << 1);
	}	
	if(r > mid){
		ans += query_interval(l,r,rt << 1 | 1);
	}
	return ans;
	
}

int main(){
	int n,m,l,r;
	char opt[10];
	while(scanf("%d %d",&n,&m) != EOF){
		for(int i = 1;i <= n;i++){
			scanf("%d",&b[i]);
		}
		build_tree(1,n,1);
		while(m--){
			scanf("%s %d %d",opt,&l,&r);
			if(opt[0] == 'a'){
				update_interval(l,r,1);
			}else{
				printf("%d\n",query_interval(l,r,1));
			}
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值