Naive Operations
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 502768/502768 K (Java/Others)
Total Submission(s): 58 Accepted Submission(s): 16
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
题目大意:给你两个长度为n的数组a、b,数组a初始化为0,数组b存1~n的随机序列
操作1:add x y 给数组a区间为x~y都加1。
操作2:quary x y 查询区间x~y内a[i] / b[i]的和,除法为整除。
解题思路:用线段树来实现区间修改,每个节点初始化为b数组的值,然后再维护区间的最小值,每次执行操作1后,就在线段树中把目标区间的值都减1,当某个区间的最小值为0的时候,就意味着该区间的某个或某些点加到了整除为1的状态,然后就往下查询是哪个点被整除,找到后再把该点的值初始化为原来的b数组的值,并且把结果+1。
我们来分析下此做法的复杂度,因为数组b是1~n的一个排列,每次操作都是把某个区间+1,仔细思考下,有许多点并没有达到能被整除的状态,所以我们可以lazy标记一下,不去更新它,再它达到能被整除的时候才去更新它,这样最坏情况下所有节点更新的次数就为(q+q/2+q/3+q/4+q/5+q/6+q/7+....+q/n)*logn(再乘以线段树区间修改时间),最后的复杂度大概就是q*logn*logn(xjb乱分析的。。)
AC代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<math.h>
#include<cstdlib>
#include<stdlib.h>
#include<queue>
#include<map>
#include<set>
#include<stack>
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define in1(a) scanf("%d" ,&a);
#define in2(a, b) scanf("%d%d", &a, &b);
#define in3(a, b, c) scanf("%d%d%d", &a, &b, &c);
#define out1(a) printf("%d\n", a);
#define out2(a, b) printf("%d %d\n", a, b);
#define pb(G, b) G.push_back(b);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const int mod = 998244353;
const LL INF = 1e9+7;
const int N = 1010;
const double pi = 3.1415926;
int n, m, mi;
struct node
{
int l;
int r;
int num; //节点当前状态,为0时意味着达到整除状态
int lazy; //懒惰标记
int sum; //整除结果
int b; //b数组初始值
}e[100010*4];
void build(int l, int r, int k)
{
e[k].l = l;
e[k].r = r;
e[k].sum = 0;
e[k].lazy = 0;
if(l == r) {
scanf("%d", &e[k].b);
e[k].sum = 0;
e[k].num = e[k].b;
return;
}
int mid = (l+r)/2;
build(l, mid, 2*k);
build(mid+1, r, 2*k+1);
e[k].num = min(e[2*k].num, e[2*k+1].num); //维护区间最小值
}
void push(int k)
{
if(e[k].lazy) {
e[2*k].num += e[k].lazy;
e[2*k+1].num += e[k].lazy;
e[2*k].lazy += e[k].lazy;
e[2*k+1].lazy += e[k].lazy;
e[k].lazy = 0;
}
}
void update(int l, int r, int k)
{
if(e[k].l == l && e[k].r == r) {
e[k].num --;
if(e[k].num) {
e[k].lazy --; //该区间没有可以整除的节点
return;
}else {
if(e[k].l == e[k].r) { //找到达到整除状态的节点
e[k].sum ++;
e[k].num = e[k].b; //更新
return;
}
}
}
push(k);
int mid = (e[k].l+e[k].r)/2;
if(r <= mid) update(l, r, 2*k);
else if(l > mid) update(l, r, 2*k+1);
else {
update(l, mid, 2*k);
update(mid+1, r, 2*k+1);
}
e[k].num = min(e[2*k].num, e[2*k+1].num); //维护区间最小值
e[k].sum = e[2*k].sum + e[2*k+1].sum;
}
int quary(int l, int r, int k)
{
if(e[k].l == l && e[k].r == r) {
return e[k].sum;
}
int mid = (e[k].l+e[k].r)/2;
if(r <= mid) return quary(l, r, 2*k);
else if(l > mid) return quary(l ,r, 2*k+1);
else {
return quary(l, mid, 2*k) + quary(mid+1, r, 2*k+1);
}
}
int main()
{
char str[10];
int x, y;
while(~scanf("%d%d", &n, &m)) {
build(1, n, 1);
for(int i = 0; i < m; i ++) {
scanf("%s%d%d", str, &x, &y);
if(str[0] == 'a') {
update(x, y, 1);
}else printf("%d\n", quary(x, y, 1));
}
}
return 0;
}