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
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?
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.
1 5 5 1 2 3 4 5 1 3 5 2 2 1 4 3 2 4 2 3 5 3 1 5
5 6
思路:很明显这道题要用线段树,关键是怎么优化,操作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));
}
}
}
}
461



被折叠的 条评论
为什么被折叠?



