[2019长沙长郡中学集训]加法

题面描述

给定一个\(n\)阶排列\(b\),要求维护一个初值全为\(0\)的数组\(\{a_i\}\),支持\(q\)次如下操作:

  • 给出\(l,r\),将\(a_l,a_{l+1},......,a_{r-1},a_r\),全部+1
  • 给出\(l,r\),查询\(\sum_{i=l}^{r} \lfloor \frac{{b[i]}}{a[i]} \rfloor\)

输入格式

第一行输入两个整数\(n,q\)
第二行\(n\)个正整数表示排列\(b\)
接下来\(q\)行,每行三个正整数\(op,l,r,op=1\)表示第一种操作,\(op=2\)表示第二种操作

输出格式

对于每次询问,输出一行一个非负整数表示答案。

数据规模

对于$%30 $的数据,\(1\leq n,q \leq 3000.\)
对于另外\(\%30\)的数据,当\(op=2\)时,有\(l=1,r=n\)
对于\(\%100\)的数据,\(1\leq n,q \leq 3\times 10^5,op\in\{1,2\},1\leq l \leq r \leq n.\)

样例输入

5 10
1 5 2 4 3
1 1 4
2 1 4
1 2 5
1 3 5
2 1 5
1 2 4
2 1 4
1 2 5
1 2 2
2 1 5

样例输出

1
2
4
6

题解

考试的时候爆掉了。。。卡常是真的恶心QAQ
先考虑部分分的做法
对于3000以内的数据,我们直接暴力循环。比较闲的同学可以考虑写两个线段树,一个用来更新a数组的值,另一个用来维护a[i]/b[i]。
对于查询操作恒查询整个区间的数据,我们可以只维护一个线段树,用于维护a数组,并区间查询整个区间的ans即可。
---------------------------以下是正解部分-----------------------------
观察原式可以发现,我们最后的取值要向下取整,则每一个位置\(i\)产生更多贡献的唯一办法就是使a[i]成为b[i]的k倍(k>0)。因此,我们用一个线段树记录b[i]的值,用数组记录b的原值,同时用另一棵线段树维护答案,每次op=1时使区间内的每一个b[i]全部-1。当b[i]=0时我们使当前位的贡献+1,并重置b[i]的值为初始值。每次单独变换的复杂度为\(O(\log n)\),每次查询时递归进第二棵线段树中查找,因此总的复杂度是\(O(q \log^2n)\)
为什么要用减法而不是加法呢?
如果用加法运算我们需要同时维护maxa和minb的值,而如果用减法只用维护一个min就好了

#include<bits/stdc++.h>
#define il inline
#define re register
typedef long long ll;
using namespace std;
const int maxn = 3e5 + 10;
int n, q, ans;
int b[maxn];
int trmin[maxn<<2], tradd[maxn<<2], lazy[maxn<<2];
//
il void init() {
	memset(lazy, 0, sizeof(lazy));
	memset(trmin, 0, sizeof(trmin));
	memset(tradd, 0, sizeof(tradd));
}
//
il void pushup(int id) {
	trmin[id] = min(trmin[id<<1], trmin[id<<1|1]);
	tradd[id] = tradd[id<<1] + tradd[id<<1|1];
	return ;
}
//
il void build(int id, int l, int r) {
	if(l == r) {
		trmin[id] = b[l];
		return;
	}
	int mid = (l+r)>>1;
	build(id<<1, l, mid);
	build(id<<1|1, mid+1, r);
	pushup(id);
}
il void pushdown(int id) {
	if (lazy[id]) {
        lazy[id << 1] += lazy[id];
        lazy[id << 1 | 1] += lazy[id];
        trmin[id << 1] -= lazy[id];
        trmin[id << 1 | 1] -= lazy[id];
        lazy[id] = 0;
    }
}
il void update(int id, int l, int r, int x, int y) {
	if(trmin[id] > 1 && x <= l && r <= y) {
		lazy[id]++;
		trmin[id]--;
		return ;
	}
	if(trmin[id] == 1 && l == r) {
		tradd[id]++;
		trmin[id] = b[l];
		lazy[id] = 0;
		return ;
	}
	pushdown(id);
	int mid = (l + r) >> 1;
    if (x <= mid)
        update(id << 1, l, mid, x, y);
    if (y > mid)
        update(id << 1 | 1, mid + 1, r, x, y);
    pushup(id);
}
il int query(int id, int l, int r, int x, int y) {
	if(x <= l && r <= y) return tradd[id];
	if(trmin[id] == 0) update(1, 1, n, x, y);
	pushdown(id);
	int sum = 0;
	int mid = (l+r) >> 1;
	if(x <= mid) sum += query(id<<1, l, mid, x, y);
	if(y > mid) sum += query(id<<1|1, mid+1, r, x, y);
	pushup(id);
	return sum;
}
signed main(){
	init();
	scanf("%d%d", &n, &q);
	for(re int i = 1; i <= n; ++i) scanf("%d", &b[i]);
	build(1, 1, n);
	int op, l, r;
	while(q--) {
		scanf("%d%d%d", &op, &l, &r);
		if(op == 1)
			update(1, 1, n, l, r);
		else
			printf("%d\n", query(1, 1, n, l, r));
	}
	return 0;
} 

posted @ 2019-09-21 14:51  迷失の风之旅人  阅读(215)  评论(0编辑  收藏  举报
123213123123