题意:
给定一个长为 n 的数组,定义值相同的区间为一子段,有 m 个操作,①:1,pos,v,修改 pos 上的值为 v;②:2,l,r,x,y,询问区间 [ l, r ] 的值域在 [ x, y ] 的段个数,只计最长段。(n, m <= 2e5)
链接:
https://nanti.jisuanke.com/t/41356
题解:
考虑将段转化为点,令 b[i] = a[i] != a[i - 1] ? a[i] : 0,则查询操作转化为求区间 [ l + 1, r ] 的值在 [ x, y ] 的元素个数(l 位置上必然满足为段,单独检查值域即可),修改则维护 b[ pos - 1 ],b[ pos ] 和 b[ pos + 1 ] 即可。显然树状数组套主席树可解(常数巨大),但我又不会 cdq 分治。 那么就愉快分块吧!考虑分块,为了愉快卡常过题,块内套树状数组,则修改操作为 O(logn)。查询涉及整块的块内 logn 查询以及角块暴力,令块大小为 nlogn,则整块数目为 nlognn,整块查询复杂度 O(nlogn),角块也是 O(nlogn),单次查询复杂度为 O(nlogn)。然后就卡过去了。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 2e5 + 5;
const int maxm = 5e2 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[maxn], b[maxn];
int li[maxm], ri[maxm], id[maxn];
int c[maxm][maxn];
int n, m;
const int maxs = 1e3 + 5;
char buf[maxs], *p1 = buf, *p2 = buf;
inline char fr(){
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, maxs, stdin)) == p1 ? -1 : *p1++;
}
#define gc fr()
inline void read(int &x){
char ch; while(!isdigit(ch = gc)); x = ch ^ 48;
while(isdigit(ch = gc)) x = x * 10 + (ch ^ 48);
}
#define lowb(x) ((x)&(-x))
void update(int x, int v, int c[]){
while(x < maxn) c[x] += v, x += lowb(x);
}
int query(int x, int c[]){
int ret = 0;
while(x) ret += c[x], x -= lowb(x);
return ret;
}
void build(){
int len = sqrt(log(n + 1) * n);
for(int i = 1; i <= n; ++i) id[i] = (i - 1) / len + 1;
for(int i = 1; i <= id[n]; ++i) li[i] = (i - 1) * len + 1, ri[i] = i * len; ri[id[n]] = n;
for(int i = 1; i <= n; ++i) if(b[i]) update(b[i], 1, c[id[i]]);
}
void Update(int x, int v){
if(v == a[x]) return;
if(x < n && v == a[x + 1]) update(b[x + 1], -1, c[id[x + 1]]), b[x + 1] = 0;
if(x > 1 && v == a[x - 1] && b[x]) update(b[x], -1, c[id[x]]), b[x] = 0;
else{
if(b[x]) update(b[x], -1, c[id[x]]);
b[x] = v, update(b[x], 1, c[id[x]]);
}
a[x] = v;
}
int Query(int l, int r, int x, int y){
if(l > r) return 0;
int p1 = id[l], p2 = id[r], ret = 0;
if(p1 == p2){
for(int i = l; i <= r; ++i) ret += b[i] >= x && b[i] <= y;
return ret;
}
for(int i = p1 + 1; i < p2; ++i) ret += query(y, c[i]) - query(x - 1, c[i]);
for(int i = l; i <= ri[p1]; ++i) ret += b[i] >= x && b[i] <= y;
for(int i = li[p2]; i <= r; ++i) ret += b[i] >= x && b[i] <= y;
return ret;
}
int main(){
read(n), read(m);
for(int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i] != a[i - 1] ? a[i] : 0;
build();
while(m--){
int opt, l, r, x, y; read(opt), read(l), read(r);
if(opt == 1) Update(l, r);
else{
read(x), read(y);
int ret = Query(l + 1, r, x, y) + (a[l] >= x && a[l] <= y);
printf("%d\n", ret);
}
}
}
cdq 分治待补。