定义:一个
i
i
i 的 “
k
k
k-后缀” 指的是字符串
S
[
i
.
.
.
k
]
S[i...k]
S[i...k](以下用
S
i
S_i
Si 表示),一个
i
i
i 的
k
k
k-后缀是好的当且仅当在
k
k
k 后方可以添加一个字符串
T
T
T 使得
S
[
i
.
.
.
k
]
+
T
≤
S
[
j
.
.
.
k
]
+
T
(
∀
j
)
S[i...k]+T\le S[j...k]+T(\forall j)
S[i...k]+T≤S[j...k]+T(∀j) 那么我们需要做的就是维护好的
k
k
k 后缀集合
引理:令
i
i
i 的
k
k
k 后缀的长度为
∣
i
∣
=
k
−
i
+
1
|i|=k-i+1
∣i∣=k−i+1,如果
i
,
j
i,j
i,j 均是好的
k
k
k 后缀,那么有
∣
i
∣
≥
2
∗
∣
j
∣
|i|\ge 2*|j|
∣i∣≥2∗∣j∣ 证明:考虑反证法,不妨令
∣
i
∣
<
2
∗
∣
j
∣
|i|<2*|j|
∣i∣<2∗∣j∣,首先
j
j
j 的
k
k
k 后缀是
i
i
i 的
k
k
k 后缀的前缀,那么容易发现
i
i
i 的
k
k
k 后缀有一个长度为
∣
i
∣
−
∣
j
∣
|i|-|j|
∣i∣−∣j∣ 的循环,设
T
T
T 可以使
S
j
S_j
Sj 为最小后缀,那么有
S
i
+
T
≥
S
j
+
T
S_i+T\ge S_j+T
Si+T≥Sj+T, 去掉
∣
i
∣
−
∣
j
∣
|i|-|j|
∣i∣−∣j∣ 的循环,有
S
j
+
T
≥
S
j
+
∣
i
∣
−
∣
j
∣
+
T
S_j+T\ge S_{j+|i|-|j|}+T
Sj+T≥Sj+∣i∣−∣j∣+T 与
S
j
S_j
Sj 为最小后缀矛盾
有了这个引理,我们可以知道一个好的
k
k
k-后缀集合大小是
l
o
g
∣
S
∣
log|S|
log∣S∣ 的,于是我们可以用线段树维护这么一个集合,单次修改需要合并
l
o
g
(
n
)
log(n)
log(n) 次,合并需要支持比较两个后缀的大小,比较
l
o
g
(
n
)
log(n)
log(n) 次,考虑二分 +
h
a
s
h
hash
hash,如果能把
h
a
s
h
hash
hash 查询做到
O
(
1
)
O(1)
O(1),那么复杂度就是
O
(
M
l
o
g
(
n
)
3
)
O(Mlog(n)^3)
O(Mlog(n)3),于是我们分块维护
h
a
s
h
hash
hash (前缀和) 即可,复杂度
O
(
n
l
o
g
(
n
)
2
+
M
l
o
g
(
n
)
3
+
M
n
)
O(nlog(n)^2+Mlog(n)^3+M\sqrt n)
O(nlog(n)2+Mlog(n)3+Mn)
主要突破口还是基于考虑好后缀的合并,然后发现更多的性质,挺巧妙的! 代码也不难写 ,写完之后一发得了 70,不知道为什么
_
_
u
i
n
t
128
_
t
\_\_uint128\_t
__uint128_t 才能过而
u
n
s
i
g
n
e
d
l
o
n
g
l
o
n
g
unsigned\ long \ long
unsignedlonglong 不行,可能是冲突了之类的(雾)
#include<bits/stdc++.h>#define cs const#define pb push_back
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch =='-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();return cnt * f;}
cs int N = 2e5 + 50;
typedef __uint128_t ull;
int n, m;
namespace Hash{
int blk[N], l[N], r[N], vl[N], add[N], ct;
ull pw[N], Spw[N], sum[N], tag[N];
cs int Base = 1e9 + 7;
void Build(){
int S = sqrt(n);
for(int i = 1; i <= n; i++) blk[i]=(i-1) / S + 1;
for(int i = 1; i <= n; i += S) l[++ct]= i, r[ct]= i + S - 1; r[ct]= n;
pw[0]= Spw[0]= 1;
for(int i = 1; i <= n; i++)
pw[i]= pw[i-1] * Base, Spw[i]= Spw[i-1] + pw[i], sum[i]= sum[i-1] + (ull)vl[i] * pw[i];}
void modify(int ql, int qr, int d){
int pl = blk[ql], pr= blk[qr];
if(pl + 1 >= pr){
for(int i = ql; i <= qr; i++){
vl[i] += d;
sum[i] += d * (Spw[i] - Spw[ql - 1]);}}
else{
for(int i = ql; i <= r[pl]; i++)
vl[i] += d, sum[i] += d * (Spw[i] - Spw[ql - 1]);
for(int i = pl + 1; i <pr; i++)
add[i] += d, tag[i] += d * (Spw[l[i] - 1] - Spw[ql - 1]);
for(int i = l[pr]; i <= qr; i++)
vl[i] += d, sum[i] += d * (Spw[i] - Spw[ql - 1]);}
ull dlt = d * (Spw[qr] - Spw[ql - 1]);
for(int i = qr + 1; i <= r[pr]; i++) sum[i] += dlt;
for(int i =pr + 1; i <= ct; i++) tag[i] += dlt;}
ull Get(int x){return sum[x] + tag[blk[x]] + add[blk[x]] * (Spw[x] - Spw[l[blk[x]]-1]);}
int val(int x){return vl[x] + add[blk[x]];}
int lcp(int x, int y){
if(x > y) swap(x, y);
int l = 0, r = n - y + 1; ull vx = Get(x - 1), vy = Get(y - 1);
while(l < r){
int mid =(l+r+1)>> 1;if((Get(x + mid -1)- vx)* pw[y - x] == Get(y + mid -1)- vy) l = mid;
else r = mid -1;
} return l;
}
}
struct data{
int l, r;
vector<int> S;
data(int _l =0, int _r =0){ S.clear(); l = _l; r = _r; }
};
data operator +(cs data &A, cs data &B){
data as(A.l, B.r);
for(int x : A.S){
bool FLAG = true;
while(as.S.size()){
int y = as.S.back();
int lcp = Hash :: lcp(x, y);
if(x + lcp - 1 >= B.r)break;
if(Hash :: val(x + lcp)> Hash :: val(y + lcp)){ FLAG =false;break;}
as.S.pop_back();}
if(FLAG &&(as.S.empty()|| B.r - x + 1 <= x - as.S.back())) as.S.pb(x);}
for(int x : B.S){
bool FLAG =true;
while(as.S.size()){
int y = as.S.back();
int lcp = Hash :: lcp(x, y);
if(x + lcp - 1 >= B.r)break;
if(Hash :: val(x + lcp)> Hash :: val(y + lcp)){ FLAG =false;break;}
as.S.pop_back();}
if(FLAG &&(as.S.empty()|| B.r - x + 1 <= x - as.S.back())) as.S.pb(x);}return as;}
namespace SGT{
cs int N = ::N << 2;#define mid ((l+r)>>1)
data vl[N];
void pushup(int x){ vl[x]= vl[x<<1] + vl[x<<1|1];}
void build(int x, int l, int r){
if(l == r){
vl[x].l = l; vl[x].r = r;
vl[x].S.pb(l);return;}
build(x<<1, l, mid); build(x<<1|1, mid+1, r);
pushup(x);}
void modify(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return;
if(L<=mid) modify(x<<1, l, mid, L, R);
if(R>mid) modify(x<<1|1, mid+1, r, L, R); pushup(x);}
data query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return vl[x];
if(R<=mid)return query(x<<1, l, mid, L, R);else if(L>mid)return query(x<<1|1, mid+1, r, L, R);return query(x<<1, l, mid, L, R) + query(x<<1|1, mid+1, r, L, R);}#undef mid}
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++) Hash :: vl[i]= read() + 5e8;
Hash :: Build();
SGT :: build(1, 1, n);
while(m--){
int op= read(), l = read(), r = read();
if(op == 1){
int d = read();
Hash :: modify(l, r, d);
SGT :: modify(1, 1, n, l, r);}
if(op == 2)
cout << SGT :: query(1, 1, n, l, r).S.back()<<'\n';}return 0;}