CF1129D
给出一个长度为 (n) 的序列 (a),把它划分成若干段,使得每一段中出现过 恰好 一次的元素个数 (le k)
求方案数对 (998244353) 取模后的结果。
(1le kle nle 10^5)
Solution
显然的 dp 式,不会的优化:
(f_i=sum f_j[Lp(j+1,i)])
其中,(Lp(j+1,i)) 表示 ([j+1,i]) 合法,不妨维护 (pre_i) 表示上一个相同颜色的位置。
考虑每次插入 (i) 时,给 (i) 处权值 (+1),给 (pre_i) 处权值改为 (-1),(pre_{pre_i}) 处权值改为 (0)
这样我们发现每次操作均为后缀修改 (+1/-1) 。
考虑我们每次查询的答案为 (S_i-S_jle k) 的点的权值和。
等价于 (S_jge S_i-k)
于是相当于要维护值域上的后缀和。
这个问题模型相当于:后缀修改,查询前缀满足 (S_jge S_i-k) 的权值和。
考虑分块,维护 (cnt_{i,j}) 表示块 (i) 权值 (j) 的出现次数。
那么 (mathcal O(nsqrt{n}log n)) 的做法是显然的,整块打标记,散块暴力重建即可,用树状数组维护每个块,这个题将绝杀,绝杀?开玩笑,这个题搞不得,这个题杀不得,他卡 (log),他卡 (log) ...
我们发现每次操作其实也会修改 (S_i-k),他每次会增大 1/-1
那么不妨考虑每次修改 (S_i-k) 的时候遍历所有块并更新所有块的答案即可。
(具体大概是对每个块维护一个标记表示 (S_i-k),那么每次整块增大就把标记左移,然后增大答案,然后修改 (S_i-k) 就右移动标记啥的)
复杂度 (mathcal O(nsqrt{n}))
注意到 (-1) 最多有 (n) 个,最好平移一下值域(或者认为所有数初始值为 (n))。
(Code:)
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int P = 998244353 ;
const int N = 1e5 + 5 ;
const int M = 322 ;
int n, K, a[N], f[N], sum[N], pre[N], w[N], Id[N], kS ;
int idx, cnt, nw[M], ans[M], tag[M], col[M][(N * 2) + 5], L[M], R[M] ;
void inc(int &x, int y) { ((x += y) >= P) && (x -= P) ; }
void dec(int &x, int y) { ((x -= y) < 0) && (x += P) ; }
void Rmove(int x) { ++ nw[x] ; if( nw[x] > 0 ) dec(ans[x], col[x][nw[x] - 1]) ; }
void Lmove(int x) { -- nw[x] ; if( nw[x] >= 0 ) inc(ans[x], col[x][nw[x]]) ; }
void Ins(int x) { ++ tag[x], Lmove(x) ; }
void Del(int x) { -- tag[x], Rmove(x) ; }
void Push(int x, int l, int r, int type) {
nw[x] = kS, ans[x] = 0 ;
rep( i, L[x], R[x] ) col[x][sum[i]] = 0, sum[i] += tag[x] ;
rep( i, l, r ) sum[i] += type ; tag[x] = 0 ;
rep( i, L[x], R[x] ) {
inc(col[x][sum[i]], f[i]) ;
if( sum[i] >= kS ) inc(ans[x], f[i]) ;
}
}
void SIns(int l) { Push(Id[l], l, R[Id[l]], 1) ; rep( i, Id[l] + 1, idx ) Ins(i) ; }
void SDel(int l) { Push(Id[l], l, R[Id[l]], -1) ; rep( i, Id[l] + 1, idx ) Del(i) ; }
void RKS() { for(re int i = 1; i <= idx; ++ i) Rmove(i) ; ++ kS ; }
void LKS() { for(re int i = 1; i <= idx; ++ i) Lmove(i) ; -- kS ; }
signed main()
{
n = gi(), K = gi() ; cnt = sqrt(n) ;
if( (n / cnt) > 300 ) cnt = n / 300 ;
rep( i, 1, n )
a[i] = gi(), pre[i] = w[a[i]], w[a[i]] = i ;
idx = 1, L[idx] = 0 ;
rep( i, 0, n ) {
Id[i] = idx ;
if( i % cnt == 0 )
R[idx] = i, L[++ idx] = i + 1 ;
}
R[idx] = n, kS = n - K ;
if( R[idx - 1] == n ) -- idx ;
rep( i, 0, n ) sum[i] = n ;
rep( i, 1, idx ) nw[i] = n - K ;
col[1][n] = 1, f[0] = 1, ans[1] = 1 ;
rep( i, 1, n ) {
int de = 0 ;
if( pre[pre[i]] ) ++ de, SIns(pre[pre[i]]) ;
if( pre[i] ) de -= 2, SDel(pre[i]), SDel(pre[i]) ;
SIns(i), ++ de ;
while( de > 0 ) RKS(), -- de ;
while( de < 0 ) LKS(), ++ de ;
rep( j, 1, idx ) inc(f[i], ans[j]) ;
Push(Id[i], L[Id[i]], R[Id[i]], 0) ;
}
cout << f[n] << endl ;
return 0 ;
}