• CF1129D


    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 ;
    }
    
  • 相关阅读:
    Minutes和TotalMinutes的区别
    C#的"?"修饰符和"??"运算符
    Navicat 连接MySQL 8.0.11 出现2059错误
    EL1004E: Method call: Method fmtdate(java.util.Date,java.lang.String) cannot be found on org.thymele
    es nested结构判断不为空
    es nested嵌套查询
    CPU基础知识线程切换
    CPU基础知识CPU的组成 运算器、控制器、寄存器
    几个常用寄存器
    Linux笔记用户态与内核态
  • 原文地址:https://www.cnblogs.com/Soulist/p/13742332.html
Copyright © 2020-2023  润新知