CF1129D
CF1129D
给出一个长度为 \(n\) 的序列 \(a\),把它划分成若干段,使得每一段中出现过 恰好 一次的元素个数 \(\le k\)
求方案数对 \(998244353\) 取模后的结果。
\(1\le k\le n\le 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_j\le k\) 的点的权值和。
等价于 \(S_j\ge S_i-k\)
于是相当于要维护值域上的后缀和。
这个问题模型相当于:后缀修改,查询前缀满足 \(S_j\ge S_i-k\) 的权值和。
考虑分块,维护 \(cnt_{i,j}\) 表示块 \(i\) 权值 \(j\) 的出现次数。
那么 \(\mathcal O(n\sqrt{n}\log n)\) 的做法是显然的,整块打标记,散块暴力重建即可,用树状数组维护每个块,这个题将绝杀,绝杀?开玩笑,这个题搞不得,这个题杀不得,他卡 \(\log\),他卡 \(\log\) ...
我们发现每次操作其实也会修改 \(S_i-k\),他每次会增大 1/-1
那么不妨考虑每次修改 \(S_i-k\) 的时候遍历所有块并更新所有块的答案即可。
(具体大概是对每个块维护一个标记表示 \(S_i-k\),那么每次整块增大就把标记左移,然后增大答案,然后修改 \(S_i-k\) 就右移动标记啥的)
复杂度 \(\mathcal O(n\sqrt{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 ;
}

浙公网安备 33010602011771号