题面描述
给定一个\(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;
}