2016多校联赛7H (hdu5828) Rikka with Sequence


Rikka with Sequence

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 631    Accepted Submission(s): 183


Problem Description
As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:

Yuta has an array A with n numbers. Then he makes m operations on it. 

There are three type of operations:

1 l r x : For each i in [l,r], change A[i] to A[i]+x
2 l r : For each i in [l,r], change A[i] to 
3 l r : Yuta wants Rikka to sum up A[i] for all i in [l,r]

It is too difficult for Rikka. Can you help her?
 

Input
The first line contains a number t(1<=t<=100), the number of the testcases. And there are no more than 5 testcases with n>1000.

For each testcase, the first line contains two numbers n,m(1<=n,m<=100000). The second line contains n numbers A[1]~A[n]. Then m lines follow, each line describe an operation.

It is guaranteed that 1<=A[i],x<=100000.
 

Output
For each operation of type 3, print a lines contains one number -- the answer of the query.
 

Sample Input
  
  
1 5 5 1 2 3 4 5 1 3 5 2 2 1 4 3 2 4 2 3 5 3 1 5
 

Sample Output
  
  
5 6
 

Author
学军中学
 

Source
 

Recommend
wange2014   |   We have carefully selected several similar problems for you:   5831  5830  5829  5827  5826 
题意:给你n个数,序列为1~n,三种操作,1是l~r之间的数全部加x,2是l~r之间的数全部开方,3求l~r之间的数的和。

思路:很明显这道题要用线段树,关键是怎么优化,操作1的优化估计大家都会,问题是操作2,第一种情况,一个数连续开方不超10次就会变成1,而1开方也还是1,那么我们只要判断区间里的数全部都是1就不用继续更新下去了,第二种情况,区间里的数全部相等,也就是区间的最大值等于最小值,那么开方之后的结果很容易就得出来了。下面给代码。

#include <map>  
#include <set>  
#include <cmath>  
#include <queue>  
#include <vector>  
#include <cstdio>  
#include <cstring>  
#include <iostream>  
#include <algorithm>  
using namespace std;
#define maxn 100005
#define ll now<<1
#define rr now<<1|1
typedef long long LL;
LL  sum[maxn << 2], minnum[maxn << 2], maxnum[maxn << 2], value[maxn << 2], addnum;
void build(int l, int r, int now){
	if (l == r){
		scanf("%lld", &sum[now]);
		maxnum[now] = minnum[now] = sum[now];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, ll);
	build(mid + 1, r, rr);
	sum[now] = sum[ll] + sum[rr];
	minnum[now] = min(minnum[ll], minnum[rr]);
	maxnum[now] = max(maxnum[ll], maxnum[rr]);
	value[now] = 0;
}
void updateadd(int l1, int r1, int l2, int r2, int now){
	if (l2 <= l1&&r2 >= r1){
		sum[now] += addnum*(r1 - l1 + 1);
		maxnum[now] += addnum;
		minnum[now] += addnum;
		value[now] += addnum;
		return;
	}
	int mid = (l1 + r1) >> 1;
	if (value[now]){
		sum[ll] += (mid - l1 + 1)*value[now];
		sum[rr] += (r1 - mid)*value[now];
		maxnum[ll] += value[now];
		maxnum[rr] += value[now];
		minnum[ll] += value[now];
		minnum[rr] += value[now];
		value[ll] += value[now];
		value[rr] += value[now];
		value[now] = 0;
	}
	if (l2 <= mid)
		updateadd(l1, mid, l2, r2, ll);
	if (r2 > mid)
		updateadd(mid + 1, r1, l2, r2, rr);
	sum[now] = sum[ll] + sum[rr];
	maxnum[now] = max(maxnum[ll], maxnum[rr]);
	minnum[now] = min(minnum[ll], minnum[rr]);
}
void updatesqrt(int l1, int r1, int l2, int r2, int now){
	if (maxnum[now] == 1){
		return;
	}
	if (l2 <= l1&&r2 >= r1&&minnum[now] == maxnum[now]){
		LL temp = sqrt(maxnum[now]);
		sum[now] = (r1 - l1 + 1)*temp;
		value[now] -= maxnum[now] - temp;
		maxnum[now] = temp;
		minnum[now] = temp;
		return;
	}
	int mid = (l1 + r1) >> 1;
	if (value[now]){
		sum[ll] += (mid - l1 + 1)*value[now];
		sum[rr] += (r1 - mid)*value[now];
		maxnum[ll] += value[now];
		maxnum[rr] += value[now];
		minnum[ll] += value[now];
		minnum[rr] += value[now];
		value[ll] += value[now];
		value[rr] += value[now];
		value[now] = 0;
	}
	if (l2 <= mid)
		updatesqrt(l1, mid, l2, r2, ll);
	if (r2 > mid)
		updatesqrt(mid + 1, r1, l2, r2, rr);
	sum[now] = sum[ll] + sum[rr];
	maxnum[now] = max(maxnum[ll], maxnum[rr]);
	minnum[now] = min(minnum[ll], minnum[rr]);
}
LL query(int l1, int r1, int l2, int r2, int now){
	if (l2 <= l1&&r2 >= r1){
		return sum[now];
	}
	int mid = (l1 + r1) >> 1;
	if (value[now]){
		sum[ll] += (mid - l1 + 1)*value[now];
		sum[rr] += (r1 - mid)*value[now];
		maxnum[ll] += value[now];
		maxnum[rr] += value[now];
		minnum[ll] += value[now];
		minnum[rr] += value[now];
		value[ll] += value[now];
		value[rr] += value[now];
		value[now] = 0;
	}
	LL lsum = 0, rsum = 0;
	if (l2 <= mid)
		lsum = query(l1, mid, l2, r2, ll);
	if (r2 > mid)
		rsum = query(mid + 1, r1, l2, r2, rr);
	return lsum + rsum;
}
int main(){
	int t;
	scanf("%d", &t);
	while (t--){
		int n, m;
		scanf("%d%d", &n, &m);
		build(1, n, 1);
		while (m--){
			int cmd, l, r;
			scanf("%d%d%d", &cmd, &l, &r);
			if (cmd == 1){
				scanf("%lld", &addnum);
				updateadd(1, n, l, r, 1);
			}
			else if (cmd == 2){
				updatesqrt(1, n, l, r, 1);
			}
			else{
				printf("%lld\n", query(1, n, l, r, 1));
			}
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值