题目
题意简述
维护一个长度为 (n) 的序列 ({a_n}),并给出 (q) 个操作:
- 将下标为 (x) 的数修改为 (y)。
- 给定 (l,r,k),求最大的 (m) 使得 ({k,k+1,dots,k+m}) 是区间 ([l,r]) 内元素的子序列。
数据规模
(n,qle10^6;~a_i,y,kle n)。
题解
首先不考虑修改操作,如何处理询问呢?
不难想到维护一列指针。令 (suf_i) 为 (i) 之后第一个满足 (a_j=a_i+1) 的 (j)。那么对于询问,相当于求从区间内的第一个 (k) 作为链头,链尾不超过 (r) 的链长。可以用 std::set
维护每个值出现的下标,再预处理出 (suf) 链的倍增,做到 (O(nlog n)-Oleft(qlog n
ight)) 求解。
这时再引入修改,自然地想到用动态树来维护一下 (suf) 链的形状。不过时间复杂度却因如下数据无法保证:若有 (a={1,1,dots,1,2}),则 (suf={n,n,dots,n,/}),再修改 (a_n) 的值,发现 (suf_1) 到 (suf_{n-1}) 的值都需要被修改啦!
为应对这一情况,我们放宽 (suf) 的限制,令 (suf_i) 为 (i) 之后第一个满足 (a_jin{a_i,a_i+1}) 的 (j),并为链加上边权:当 (a_j=a_i),没有贡献,边权为 (0),否则边权为 (1)。实际上,由于每个点仅会向右连一条边,可以把 (suf_i) 边权作为 (i) 的点权。并加入虚拟结点 (n+1) 作为动态树的根,对于任意 (suf_i),若没有满足条件的 (j),则它指向 (n+1)。
这样一来,我们就有能力处理修改与查询啦。
- 查询操作:先找到区间中的第一个 (k) 所对应的下标 (u),在 LCT 中提取 (u ightarrow n+1) 这条路径(由 (suf) 的定义,一定存在),并在路径 Splay 上查找 (r) 的前驱结点 (v) ——由于 (n+1) 为根,所以深度越浅,结点编号越大,所以查前驱的时候需要把 Splay 当做“左大右小”的平衡树。最后,提取路径 (u ightarrow v),计算子树和,注意判断 (v) 结点是否产生贡献。
- 修改操作:分别考虑去掉该点所产生的印象和加入该点所产生的印象。利用
std::set
进行lower_bound
等操作即可。
代码
由于码的时候思路比较混乱,所以没有封装,而且压行有些丑呢 qwq。
#include <set>
#include <cstdio>
#include <assert.h>
#include <iostream>
typedef std :: set<int> :: iterator IT;
inline int rint () {
int x = 0, f = 1; char s = getchar ();
for ( ; s < '0' || '9' < s; s = getchar () ) f = s == '-' ? -f : f;
for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
return x * f;
}
template<typename Tp>
inline void wint ( Tp x ) {
if ( x < 0 ) putchar ( '-' ), x = ~ x + 1;
if ( 9 < x ) wint ( x / 10 );
putchar ( x % 10 ^ '0' );
}
const int MAXN = 2e6;
int n, q, a[MAXN + 5], suf[MAXN + 5];
std :: set<int> apr[MAXN + 5];
int ch[MAXN + 5][2], fa[MAXN + 5], val[MAXN + 5], siz[MAXN + 5], sum[MAXN + 5];
bool flip[MAXN + 5];
inline bool nroot ( const int x ) { return ch[fa[x]][0] == x || ch[fa[x]][1] == x; }
inline void pushrv ( const int x ) { flip[x] ^= 1, ch[x][0] ^= ch[x][1] ^= ch[x][0] ^= ch[x][1]; }
inline void pushup ( const int x ) { sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + val[x]; }
inline void pushdn ( const int x ) {
if ( ! flip[x] ) return ;
if ( ch[x][0] ) pushrv ( ch[x][0] );
if ( ch[x][1] ) pushrv ( ch[x][1] );
flip[x] = false;
}
inline void rotate ( const int x ) {
int y = fa[x], z = fa[y], k = ch[y][1] == x;
fa[x] = z; if ( nroot ( y ) ) ch[z][ch[z][1] == y] = x;
ch[y][k] = ch[x][k ^ 1]; if ( ch[x][k ^ 1] ) fa[ch[x][k ^ 1]] = y;
ch[x][k ^ 1] = y, fa[y] = x, pushup ( y ), pushup ( x );
}
inline void splay ( const int x ) {
static int y, z, stk[MAXN + 5];
for ( stk[z = 1] = y = x; nroot ( y ); stk[++ z] = y = fa[y] );
for ( ; z; pushdn ( stk[z --] ) );
for ( ; nroot ( x ); rotate ( x ) ) {
if ( nroot ( y = fa[x] ) ) {
rotate ( x ^ y ^ ch[y][0] ^ ch[fa[y]][0] ? x : y );
}
}
pushup ( x );
}
inline void access ( int x ) { for ( int y = 0; x; x = fa[y = x] ) splay ( x ), ch[x][1] = y, pushup ( x ); }
inline void makeRoot ( const int x ) { access ( x ), splay ( x ), pushrv ( x ); }
inline void link ( const int x, const int y ) { makeRoot ( x ), fa[x] = y; }
inline void cut ( const int x, const int y ) { makeRoot ( x ), access ( y ), splay ( x ), fa[y] = ch[x][1] = 0, pushup ( x ); }
inline void find ( int& u, const int k ) { for ( pushdn ( u ); ch[u][k < u] && k ^ u; pushdn ( u = ch[u][k < u] ) ); splay ( u ); }
inline int getPre ( int u, const int k ) {
find ( u, k ); if ( u <= k ) return u;
pushdn ( u ), u = ch[u][1];
for ( pushdn ( u ); ch[u][0]; u = ch[u][0], pushdn ( u ) );
return u;
}
inline void linksuf ( const int i, const int k ) {
IT it1 ( apr[k].upper_bound ( i ) ), it2 ( apr[k + 1].upper_bound ( i ) );
suf[i] = std :: min ( *it1, *it2 );
val[i] = suf[i] == *it2, pushup ( i ), link ( i, suf[i] );
}
inline void relink ( const int i, const int k ) {
IT it = apr[k].lower_bound ( i );
if ( it != apr[k].begin () ) {
-- it;
cut ( *it, suf[*it] );
linksuf ( *it, a[*it] );
}
}
int main () {
n = rint (), q = rint ();
for ( int i = 1; i <= n; ++ i ) apr[a[i] = rint ()].insert ( i );
for ( int i = 0; i <= n + 1; ++ i ) apr[i].insert ( n + 1 );
val[n + 1] = 0, pushup ( n + 1 );
for ( int i = 1; i <= n; ++ i ) linksuf ( i, a[i] );
for ( int opt, l, r; q --; ) {
opt = rint (), l = rint (), r = rint ();
if ( opt & 1 ) {
int t = a[l]; a[l] = r;
apr[t].erase ( l ), apr[r].insert ( l );
relink ( l, t ), relink ( l, t - 1 ), relink ( l, r ), relink ( l, r - 1 );
cut ( l, suf[l] ), linksuf ( l, a[l] );
} else {
int p = *apr[rint ()].lower_bound ( l ), q;
if ( p > r ) { puts ( "-1" ); continue; }
makeRoot ( n + 1 ), access ( p ), splay ( p );
q = getPre ( p, r );
makeRoot ( p ), access ( q ), splay ( q );
wint ( sum[q] - val[q] ), putchar ( '
' );
}
}
return 0;
}