「数列分块入门」系列

谈到分块,不得不说说 $\text{hzwer}$ 学长的「数列分块入门」系列。

若未特别指出$\text{ML}$或$\text{TL}$,则$\text{ML:256MB,TL:500ms}$。

数列分块入门 1

[LOJ 6277]

题意

给出一个长为$n$的数列,有$n$个操作,操作涉及区间加法,单点查值。

$1\leq l\leq r\leq n\leq 50000$,输入的所有数均为$\text{int}$范围的整数。

$\text{TL:100ms}$

思路

啥?这题还要思路?

代码

码风丑见谅:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)+1;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)-1;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define PII pair<int,int>
#define MP make_pair
typedef long long LL;
typedef double DB;
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
const int mod = 998244353 , N = 50010 , SN = 230;
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
inline int gcd(int x,int y){ if(x<y) swap(x,y); return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
}
using namespace my_std;

int n,blo;
int a[N],bl[N],tag[SN];

inline void update(int l,int r,int x){
Rint tem = min(bl[l] * blo , r);
FOR(i,l,tem) a[i] += x;
if(bl[l] != bl[r]) FOR(i,(bl[r] - 1) * blo + 1,r) a[i] += x;
FOR(i,bl[l] + 1,bl[r] - 1) tag[i] += x;
return;
}

int main(){
n = read(),blo = sqrt(n);
FOR(i,1,n) a[i] = read();
FOR(i,1,n) bl[i] = (i - 1)/blo + 1;
Rint opt,l,r,c;
FOR(i,1,n){
opt = read() , l = read() , r = read() , c = read();
opt ? (void)(printf("%d\n",a[r] + tag[bl[r]])) : update(l,r,c);
}
return 0;
}

数列分块入门 2

[LOJ 6278]

题意

给出一个长度为$n$的数列,以及$n$个操作,操作涉及区间加法,询问区间内小于某个值$x$的元素个数。

$1\leq l\leq r\leq n\leq 50000$,输入的所有数均为$\text{int}$范围的整数。

思路

将数列分成长为$S$的块。

修改就对整块打$\text{tag}$,对不完整的块暴力修改、排序,复杂度$O(\lfloor\frac{n}{S}\rfloor+S\log_2S)$。

询问就对整块$\text{lower_bound}$,对不完整的块暴力,复杂度$O(\lfloor\frac{n}{S}\rfloor\log_2S+S)$。

然后平衡一下,$S$ 取$\sqrt{n}$就好了。

代码

こ↑こ↓
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)+1;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)-1;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define PII pair<int,int>
#define MP make_pair
typedef long long LL;
typedef double DB;
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
const int mod = 998244353 , N = 50010 , SN = 250;
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
inline int gcd(int x,int y){ if(x<y) swap(x,y); return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
}
using namespace my_std;

int n,blo,bl[N],bid[SN];
LL a[N],b[N],tag[SN];

inline int query(int l,int r,LL val){
Rint lb = bl[l] , rb = bl[r] , res = 0;
if(lb == rb){
FOR(i,l,r) res += a[i] < (val - tag[lb]);
return res;
}
FOR(i,lb + 1,rb - 1) res += lower_bound(b + bid[i - 1] + 1,b + bid[i] + 1,val - tag[i]) - b - bid[i - 1] - 1;
FOR(i,l,bid[lb]) res += a[i] < (val - tag[lb]);
ROF(i,r,bid[rb - 1] + 1) res += a[i] < (val - tag[rb]);
return res;
}

inline void update(int l,int r,LL val){
Rint lb = bl[l] , rb = bl[r];
if(lb == rb){
FOR(i,bid[lb - 1] + 1,bid[lb]){
if(l <= i && i <= r) a[i] += val;
b[i] = a[i];
}
return sort(b + bid[lb - 1] + 1,b + bid[lb] + 1);
}
FOR(i,lb + 1,rb - 1) tag[i] += val;
FOR(i,bid[lb - 1] + 1,bid[lb]){
if(l <= i) a[i] += val;
b[i] = a[i];
}
FOR(i,bid[rb - 1] + 1,bid[rb]){
if(i <= r) a[i] += val;
b[i] = a[i];
}
return sort(b + bid[lb - 1] + 1,b + bid[lb] + 1),sort(b + bid[rb - 1] + 1,b + bid[rb] + 1);
}

int main(){
n = read() , blo = sqrt(n);
FOR(i,1,n) b[i] = a[i] = read();
Rint opt,l,r,c = 1;
FOR(i,1,n - 1){
bl[i] = c;
if(!(i % blo)) bid[c++] = i;
}
bl[n] = c , bid[c] = n;
FOR(i,1,c) sort(b + bid[i - 1] + 1,b + bid[i] + 1);
FOR(i,1,n){
opt = read() , l = read() , r = read() , c = read();
if(opt) printf("%d\n",query(l,r,1ll * c * c));
else update(l,r,1ll * c);
}
return 0;
}

数列分块入门 3

[LOJ 6279]

题意

给出一个长为$n$的数列,以及$n$个操作,操作涉及区间加法,询问区间内$x$的前驱。

$1\leq l\leq r\leq n\leq 100000$,输入的所有数均为$\text{int}$范围的整数。

$\text{TL:1500ms}$

思路

基本同上「数列分块入门 2」,但是不能直接排序(复杂度$O(n\sqrt{n}\log_2n)$)而是需要用一些数据结构存储每个块的信息。

本人代码使用$set$存储每个块的信息。

微妙地调整块大小应该可以过吧……本人代码采用的是$S=n^{0.612}$

代码

こ↑こ↓
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)+1;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)-1;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define PII pair<int,int>
#define MP make_pair
typedef long long LL;
typedef double DB;
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
const int mod = 998244353 , N = 100010 , SN = 330;
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
inline int gcd(int x,int y){ if(x<y) swap(x,y); return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
}
using namespace my_std;

int n,blo,a[N],bl[N],bid[SN],tag[SN];
set<int> s[SN];

inline void update(int l,int r,int v){
Rint lb = bl[l] , rb = bl[r];
if(lb == rb){
FOR(i,l,r){
s[lb].erase(a[i]);
a[i] += v;
s[lb].insert(a[i]);
}
return;
}
FOR(i,lb + 1,rb - 1) tag[i] += v;
FOR(i,l,bid[lb]) s[lb].erase(a[i]),a[i] += v,s[lb].insert(a[i]);
ROF(i,r,bid[rb-1]+1) s[rb].erase(a[i]),a[i] += v,s[rb].insert(a[i]);
return;
}

inline int query(int l,int r,int v){
Rint lb = bl[l] , rb = bl[r] , ans = INT_MIN;
if(lb == rb){
FOR(i,l,r) if(a[i] + tag[lb] < v && ans < a[i] + tag[lb]) ans = a[i] + tag[lb];
return ans > INT_MIN ? ans : -1 ;
}
FOR(i,lb + 1,rb - 1){
reg auto it = s[i].lower_bound(v - tag[i]);//set<int>::iterator
if(it == s[i].begin()) continue;
--it;
if(ans < *it + tag[i]) ans = *it + tag[i];
}
FOR(i,l,bid[lb]) if(a[i] + tag[lb] < v && ans < a[i] + tag[lb]) ans = a[i] + tag[lb];
ROF(i,r,bid[rb-1]+1) if(a[i] + tag[rb] < v && ans < a[i] + tag[rb]) ans = a[i] + tag[rb];
return ans > INT_MIN ? ans : -1;
}

int main(){
n = read() , blo = ceil(pow(n,0.612));
FOR(i,1,n) a[i] = read();
Rint opt,l,r,c = 1;
FOR(i,1,n - 1){
bl[i] = c;
if(!(i % blo)) bid[c++] = i;
}
bl[n] = c , bid[c] = n;
FOR(i,1,n) s[bl[i]].insert(a[i]);
FOR(i,1,n){
opt = read() , l = read() , r = read() , c = read();
if(opt) printf("%d\n",query(l,r,c));
else update(l,r,c);
}
return 0;
}

数列分块入门 4

[LOJ 6280]

题意

给出一个长为$n$的数列,以及$n$个操作,操作涉及区间加法,区间求和并对$c$取模(每个询问给出)。

$1\leq l\leq r\leq n\leq 50000,c>0$,输入的所有数均为$\text{int}$范围的整数。

思路

经典问题,经典中的经典。

代码

こ↑こ↓
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)+1;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)-1;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define PII pair<int,int>
#define MP make_pair
typedef long long LL;
typedef double DB;
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
const int mod = 998244353 , N = 50010 , SN = 250;
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
inline int gcd(int x,int y){ if(x<y) swap(x,y); return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
}
using namespace my_std;

int n,blo,bl[N],bid[SN];
LL a[N],tag[SN],sum[SN];

int query(int l,int r,LL p){
Rint lb = bl[l] , rb = bl[r];
reg LL ans = 0;
if(lb == rb){
FOR(i,l,r) ans += a[i] + tag[lb];
return (int)(((ans %= (p + 1)) < 0) ? ans + p + 1 : ans);
}
FOR(i,lb + 1,rb - 1) ans += sum[i];
FOR(i,l,bid[lb]) ans += a[i] + tag[lb];
ROF(i,r,bid[rb-1]+1) ans += a[i] + tag[rb];
return (int)(((ans %= (p + 1)) < 0) ? ans + p + 1 : ans);
}

inline void update(int l,int r,LL val){
Rint lb = bl[l] , rb = bl[r];
if(lb == rb){
FOR(i,l,r) a[i] += val , sum[lb] += val;
return;
}
FOR(i,lb + 1,rb - 1) tag[i] += val , sum[i] += val * blo;
FOR(i,l,bid[lb]) a[i] += val , sum[lb] += val;
ROF(i,r,bid[rb-1]+1) a[i] += val , sum[rb] += val;
return;
}

int main(){
// FILE("a13");
n = read() , blo = sqrt(n);
FOR(i,1,n) a[i] = read();
Rint opt,l,r,c = 1;
FOR(i,1,n - 1){
bl[i] = c;
if(!(i % blo)) bid[c++] = i;
}
bl[n] = c,bid[c] = n;
FOR(i,1,n) i[bl][sum] += a[i];
FOR(i,1,n){
opt = read() , l = read() , r = read() , c = read();
opt ? (void)(printf("%d\n",query(l,r,(LL)c))) : update(l,r,(LL)c);
}
return 0;
}

数列分块入门 5

[LOJ 6281]

题意

给出一个长为$n$的数列,以及$n$个操作,操作涉及区间开方,区间求和。

$1\leq l\leq r\leq n\leq 50000$,输入的所有数均为$\text{int}$范围的自然数。

思路

什么这不是线段树题吗

代码

只写了线段树的代码,就不放了……

数列分块入门 6

[LOJ 6282]

题意

给出一个长度为$n$的数列,以及$n$个操作,操作涉及单点插入,单点查询。

$1\leq l\leq r\leq n\leq 100000$,输入的所有数均为$\text{int}$范围的整数。

输入输出:

第一行,一个数字$n$。

第二行$n$个数字,第$i$个数字为$a_i$。

接下来$n$行操作,每组操作输入四个数字$opt,l,r,c$。

若$opt=0$,表示在第$l$个数字前插入数字$r$。

若$opt=1$,表示询问$a_r$的值。

对每组$opt=1$的询问,输出一行一个整数,为$a_r$的值。

什么?你问我c干啥用的?我也想知道

思路

先把原序列分块,块大小$S$,块数$Bl$。

使用$\text{vector}$存储每一块信息。插入时暴力插入,将该块后面的元素都后移一位,复杂度$O(S)$;查询也是暴力,复杂度$O(Bl)$。

但是可能在一个块里有大量插入,此时该块大小将远远超过$S$,复杂度退化。

可以当某个块过大时,将该块分为两个,暴力重构,时间复杂度$O(n)$(若使用链表,可以锐减至$O(S+Bl)$。)

也可以考虑每$m$次插入后对序列进行重构,复杂度也为$O(n)$。

喂这种要”保持平衡”的思想不是和平衡树差不多吗你怎么不写棵平衡树

代码

本人采用的是$S=\sqrt{n}$,在块大小不小于$2S$时将该块分为两个,暴力拆块,复杂度$O(n)$。

由于最多只会拆$\sqrt{n}$次块,拆块复杂度$O(n\sqrt{n})$,可以通过此题。

指针恐惧症慎入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)+1;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)-1;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define pb push_back
#define PII pair<int,int>
#define MP make_pair
typedef long long LL;
typedef double DB;
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
const int mod = 998244353 , N = 200010 , SN = 650;
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
inline int gcd(int x,int y){ if(x<y) swap(x,y); return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
}
using namespace my_std;

int n,blo,bn;
vector<int>a[SN];
#define rsiz(h) ((int)((h)->size()))

inline void update(int t,int val){//在序号t的数后面插入数val
reg vector<int> *k = a;
while(rsiz(k + 1) <= t && (k - a) < bn) ++k,t -= rsiz(k);
++k;
if((k - a) > bn) return;
if(t < 0) return;
k -> insert((k -> begin()) + t,val);
if(rsiz(k) >= 2 * blo){
ROF(i,bn,k - a + 1) a[i + 1] = a[i];
(k + 1) -> clear();
FOR(i,blo,rsiz(k) - 1) (k + 1) -> pb((*k)[i]);
while(rsiz(k) > blo) k -> erase((k -> end()) - 1);
++bn;
}
return;
}
inline int query(int t){//查找序号为t的数
--t;
reg vector<int> *k = a;
while(rsiz(k + 1) <= t && (k - a) < bn) ++k,t -= rsiz(k);
++k;
if((k - a) > bn) return 0;
if(t < 0) return 0;
return (*k)[t];
}

int main(){
n = read() , blo = sqrt(n);
Rint opt,l,r = 0,c = 1;
bn = ceil(sqrt(n));
FOR(i,1,n){
a[c].pb(read());
++r;
if(r == blo) ++c,r = 0;
}
FOR(i,1,n){
opt = read() , l = read() , r = read() , read();
opt ? (void)(printf("%d\n",query(r))) : update(l - 1,r);
// printf("Siz : "); FOR(i,1,bn) printf("%d ",rsiz(a + i)); puts("");
// FOR(i,1,bn){ printf("a[%d] : ",i); FOR(j,0,rsiz(a + i) - 1) printf("%d ",a[i][j]); puts(""); }
}
return 0;
}

数列分块入门 7

[LOJ 6283]

题意

给出一个长为$n$的数列,以及$n$操作,操作涉及区间加法,区间乘法,单点询问。

$1\leq l\leq r\leq n\leq 100000$,输入的所有数均为$\text{int}$范围的整数,询问对$10007$取模。

所以为什么不是查询区间和

思路

右转线段树模板,请

反正分块的思路基本相同,只是tag不用下传

代码

只写了线段树,所以不贴了。

数列分块入门 8

[LOJ 6284]

题意

给出一个长为$n$的数列,以及$n$个操作,操作涉及区间询问等于一个数 的元素,并将这个区间的所有元素改为$c$。

$1\leq l\leq r\leq n\leq 100000$,输入的所有数均为$\text{int}$范围的整数。

输入输出:

第一行,一个数字$n$。

第二行,$n$个数字,第$i$个为$a_i$。

接下来输入$n$行询问,每行三个数字$l,r,c$。

表示先查询位于$[l,r]$的数字有多少个是$c$,再将$[l,r]$内的数字都改为$c$。

数据不随机。

思路

分块,块大小$\sqrt{n}$,每块记录一下是否全部相等,若是则$O(1)$处理,不是则暴力扫一遍。不完整的块也暴力处理。

这样复杂度看上去最坏是$O(n)$的。然而,当经过了一次$O(n)$的询问后,几乎所有块都变得相等了。而一次操作最多将$2$个块变得不等,所以至少需要$\sqrt{n}$次操作后才可能再次出现$O(n)$的最坏复杂度。综合一下,可得复杂度是$O(n\sqrt{n})$,可以通过此题。

本人的说法可能不够严谨,希望有人可以提供更好的解释方法!

代码

こ↑こ↓
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)+1;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)-1;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define PII pair<int,int>
#define MP make_pair
typedef long long LL;
typedef double DB;
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
const int mod = 998244353 , N = 100010 , SN = 450;
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
inline int gcd(int x,int y){ if(x<y) swap(x,y); return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
}
using namespace my_std;

int n,blo,a[N],sam[SN],bl[N],bid[SN];

inline void update(int b){//更新某个块状态
if(!sam[b]) return;
FOR(i,bid[b - 1] + 1,bid[b] - 1) a[i] = b[bid][a];
return (void)(sam[b] = 0);
}

inline int slove(int l,int r,int val){
Rint lb = bl[l] , rb = bl[r] , res = 0;
if(lb == rb){
update(lb);
FOR(i,l,r) (a[i] == val) ? (++res) : (a[i] = val) ;
sam[lb] = (l == bid[lb - 1] + 1 && r == bid[lb]);
return res;
}
update(lb);
ROF(i,bid[lb],l) (a[i] == val) ? (++res) : (a[i] = val) ;
sam[lb] = (l == bid[lb - 1] + 1);
update(rb);
FOR(i,bid[rb-1]+1,r) (a[i] == val) ? (++res) : (a[i] = val) ;
sam[rb] = (r == bid[rb]);
FOR(i,lb + 1,rb - 1){
if(sam[i]){
(i[bid][a] == val) ? (res += blo) : (i[bid][a] = val);
continue;
}
FOR(j,bid[i - 1] + 1,bid[i]) (a[j] == val) ? ++res : a[j] = val ;
sam[i] = 1;
}
return res;
}

int main(){
n = read() , blo = sqrt(n);
Rint l,r,c = 1;
FOR(i,1,n - 1){
a[i] = read() , bl[i] = c;
if(!(i % blo)) sam[c] = 1,bid[c++] = i;
}
a[n] = read() , bl[n] = c , bid[c] = n;
FOR(i,1,c) FOR(j,bid[i-1] + 1,bid[i] - 1) if(a[j] != i[bid][a]){ sam[i] = 0; break; }
FOR(i,1,n){
l = read() , r = read() , c = read();
printf("%d\n",slove(l,r,c));
}
return 0;
}

加强

啊说起来本题还可以加强的来着。

给出一个长为$n$的数列,以及$n$个操作,操作涉及区间询问等于一个数 的元素,或将这个区间的所有元素改为$c$。

具体地:

第一行,一个整数$n$。

第二行,$n$个整数,第$i$个为$a_i$。

接下来输入$n$行询问,每行四个整数$opt,l,r,c$。

若$opt=0$,表示将$[l,r]$内的所有数改为$c$。

若$opt=1$,输出一行一个整数,表示$[l,r]$内有多少个数是$c$。

数据不随机

  1. $1\leq l\leq r\leq n\leq 100000$,所有输入的数均在$\text{int}$范围内,不强制在线。
  2. $1\leq l\leq r\leq n\leq 50000$,所有输入的数均在$\text{int}$范围内,强制在线

$\text{TL:1000ms}$

思路

好像和原题没什么关系了……

不强制在线的话,可以先离散化,然后分成$\sqrt{n}$大小的块。

每个块用$O(n)$的空间存储每种数的数量,如果全部相同则打$\text{tag}$。

首先,单点修改是$O(1)$的(某种数的数量$+1$,某种数的数量$-1$),整块修改也是$O(1)$的(打$\text{tag}$)。

不完整的块的修改则是$O(\sqrt{n})$的(可能需要拆$\text{tag}$,拆掉后相当于整个块进行修改,也是$O(\sqrt{n})$的)。

查询时,显然对于整块是$O(1)$的,对于不完整的块暴力是$O(\sqrt{n})$的。

所以时间复杂度和空间复杂度都是$O(n\sqrt{n})$,可以通过此题。

强制在线的话,可以用$\text{map}$来使空间复杂度保持$O(n\sqrt{n})$,而时间复杂度将变为$O(n\sqrt{n}\log_2n)$,大概应该或许能通过此题

因加强版未找到题目,对于强制在线的时间复杂度,若能在保证空间的情况下做到更优,欢迎在评论区指出或私信本人!

代码

こ↑こ↓
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define reg register
#define Rint register int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define FORit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)+1;++i)
#define ROFit(templ,arr,i,a,b) for(register templ *i=(arr)+(a);i!=(arr)+(b)-1;--i)
#define GO(x,p,e,i,v) for(register int i=p[x].head,v;i;i=e[i].link)
#define MEM(x,v) memset(x,v,sizeof(x))
#define fir first
#define sec second
#define pq priority_queue
#define PII pair<int,int>
#define MP make_pair
typedef long long LL;
typedef double DB;
inline int read(){
int ans=0,f=1;char c=getchar();
while(!isdigit(c)){ f^=(c=='-'); c=getchar(); }
for(;isdigit(c);c=getchar()) ans=(ans<<1)+(ans<<3)+(c^48); return f?ans:-ans;
}
const int mod = 998244353 , N = 100010 , SN = 280;
inline void inc(int &x,const int &y){ x+=y; if(x>=mod) x-=mod; }
inline int ksm(int x,LL y){ int res=1; for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) res=1ll*res*x%mod; return res;}
inline int gcd(int x,int y){ if(x<y) swap(x,y); return y?gcd(y,x%y):x; }
#define FILE(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
}
using namespace my_std;

int n,blo,a[N],b[N << 1],bl[N],bid[SN],siz,sam[SN];
int num[SN][N << 1],ql[N],qr[N],qc[N];

inline void check(int b){
if(!sam[b]) return;
Rint v = b[bid][a];
FOR(i,bid[b - 1] + 1,bid[b] - 1) --num[b][a[i]] , a[i] = v , ++num[b][v];
return;
}
inline int query(int l,int r,int val){
Rint lb = bl[l] , rb = bl[r] , res = 0;
check(lb);
if(lb == rb){
FOR(i,l,r) res += (a[i] == val);
return res;
}
check(rb);
FOR(i,l,bid[lb]) res += (a[i] == val);
ROF(i,r,bid[rb-1]+1) res += (a[i] == val);
FOR(i,lb + 1,rb - 1) res += sam[i] ? ((i[bid][a] == val) ? blo : 0) : num[i][val] ;
return res;
}
inline void update(int l,int r,int val){
Rint lb = bl[l] , rb = bl[r];
if(lb == rb){
if(sam[lb]){
FOR(i,bid[lb - 1] + 1,bid[lb]){
--num[lb][a[i]];
a[i] = (l <= i && i <= r) ? val : lb[bid][a] ;
++num[lb][a[i]];
}
return (void)(sam[lb] = (l == bid[lb - 1] + 1 && r == bid[lb]));
}
FOR(i,l,r) --num[lb][a[i]] , a[i] = val , ++num[lb][a[i]];
return (void)(sam[lb] = (l == bid[lb - 1] + 1 && r == bid[lb]));
}
if(sam[lb]){
FOR(i,bid[lb - 1] + 1,bid[lb]) --num[lb][a[i]] , a[i] = ((l <= i) ? val : lb[bid][a]) , ++num[lb][a[i]];
}
else FOR(i,l,bid[lb]) --num[lb][a[i]] , a[i] = val , ++num[lb][val];
if(sam[rb]){
FOR(i,bid[rb - 1] + 1,bid[rb]) --num[rb][a[i]] , a[i] = ((i <= r) ? val : rb[bid][a]) , ++num[rb][a[i]];
}
else FOR(i,bid[rb-1]+1,r) --num[rb][a[i]] , a[i] = val , ++num[rb][val];
sam[lb] = (l == bid[lb - 1] + 1) , sam[rb] = (r == bid[rb]);
FOR(i,lb + 1,rb - 1){
sam[i] = 1;
--num[i][i[bid][a]] , i[bid][a] = val , ++num[i][val];
}
return;
}


int main(){
n = read() , blo = ceil(pow(n,0.514)) , siz = n;
// return printf("%d %d\n",blo,(int)(ceil(n / (DB)blo))),0;
Rint t = 1;
FOR(i,1,n - 1){
b[i] = a[i] = read() , bl[i] = t;
if(!(i % blo)) bid[t++] = i;
}
b[n] = a[n] = read() , bl[n] = t , bid[t] = n;
FOR(i,1,n) ql[i] = read() , qr[i] = read() , qc[i] = read() , b[++siz] = qc[i];
sort(b + 1,b + siz + 1);
siz = unique(b + 1,b + siz + 1) - b - 1;
FOR(i,1,n){
a[i] = lower_bound(b + 1,b + siz + 1,a[i]) - b;
qc[i] = lower_bound(b + 1,b + siz + 1,qc[i]) - b;
t = bl[i];
num[t][a[i]]++;
if(i == bid[t] && num[t][a[i]] == bid[t] - bid[t - 1]) sam[t] = 1;
}
FOR(i,1,n){
printf("%d\n",query(ql[i],qr[i],qc[i]));
update(ql[i],qr[i],qc[i]);
#ifdef NTF_AK_IOI
printf("Maybe the array : "); FOR(i,1,n) printf("%d ",a[i]); puts("");
FOR(i,1,bl[n]){
printf("block[%d] : ",i);
if(sam[i]){ printf("same %d\n",i[bid][a]); continue; }
FOR(j,1,siz) printf("%d ",num[i][j]); puts("");
}
#endif
}
return 0;
}

数列分块入门 9

[LOJ 6285]

题意

给出一个长为$n$的数列,以及$n$个询问,询问涉及求区间最小众数。

$1\leq l\leq r\leq n\leq 100000$,输入的所有数均为$\text{int}$范围的整数,不强制在线

思路

离线下来,做个莫队,块大小$\lfloor\frac{n}{\sqrt{n\log_2n}}\rfloor$。

具体实现我不会……啊啊我好菜

代码

我不会啊

加强

强制在线。见强制在线不带修区间众数