Naive Operations
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 502768/502768 K (Java/Others)
Total Submission(s): 1659 Accepted Submission(s): 709
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
Recommend
chendu | We have carefully selected several similar problems for you: 6318 6317 6316 6315 6314
【思路】
多校第二场,应该开出来的第三题,很遗憾没能做出来,思路十分厉害,补记之。
不是每一次更新都要去对应的点来确认是否加一的,只需要在每个点被修改bi次时才需要加一。鉴于序列是一个1到n的排列,当满打满算时分子最终为q,整个序列就成了调和级数,因此用线段树维护,实质在叶子节点的更新次数也是对数级别的。
线段树维护的区间和sum与每个节点还有几次才加一这个数字的区间最小值min,每次更新就区间减一,当区间被减到0时,就dfs到它的叶子节点把他的sum加一,再把min改为。
【代码】
//******************************************************************************
// File Name: 1007.cpp
// Author: Shili_Xu
// E-Mail: shili_xu@qq.com
// Created Time: 2018年07月26日 星期四 14时00分47秒
//******************************************************************************
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 101000;
struct segment {
int sum, min, flag;
};
int n, q;
int b[MAXN];
segment tree[MAXN << 2];
void push_down(int rt)
{
if (tree[rt].flag) {
tree[rt << 1].min += tree[rt].flag;
tree[rt << 1].flag += tree[rt].flag;
tree[rt << 1 | 1].min += tree[rt].flag;
tree[rt << 1 | 1].flag += tree[rt].flag;
tree[rt].flag = 0;
}
}
void update(int rt)
{
tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
tree[rt].min = min(tree[rt << 1].min, tree[rt << 1 | 1].min);
}
void build(int left, int right, int rt)
{
tree[rt].flag = 0;
if (left == right) {
tree[rt].min = b[left];
tree[rt].sum = 0;
return;
}
int mid = (left + right) >> 1;
build(left, mid, rt << 1);
build(mid + 1, right, rt << 1 | 1);
update(rt);
}
void modify(int l, int r, int x, int left, int right, int rt)
{
int mid = (left + right) >> 1;
if (l <= left && right <= r) {
tree[rt].min += x;
tree[rt].flag += x;
if (!tree[rt].min) {
if (left == right) {
tree[rt].sum++;
tree[rt].min = b[left];
return;
}
push_down(rt);
if (!tree[rt << 1].min) modify(l, r, 0, left, mid, rt << 1);
if (!tree[rt << 1 | 1].min) modify(l, r, 0, mid + 1, right, rt << 1 | 1);
update(rt);
}
return;
}
push_down(rt);
if (l <= mid) modify(l, r, x, left, mid, rt << 1);
if (r >= mid + 1) modify(l, r, x, mid + 1, right, rt << 1 | 1);
update(rt);
}
int query(int l, int r, int left, int right, int rt)
{
if (l <= left && right <= r) return tree[rt].sum;
push_down(rt);
int mid = (left + right) >> 1;
int ans = 0;
if (l <= mid) ans += query(l, r, left, mid, rt << 1);
if (r >= mid + 1) ans += query(l, r, mid + 1, right, rt << 1 | 1);
return ans;
}
int main()
{
while (scanf("%d %d", &n, &q) == 2) {
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
build(1, n, 1);
while (q--) {
char mes[20];
int l, r;
scanf("%s %d %d", mes, &l, &r);
if (mes[0] == 'a')
modify(l, r, -1, 1, n, 1);
else {
int ans = query(l, r, 1, n, 1);
printf("%d\n", ans);
}
}
}
return 0;
}