• 【题解】 CF750E New Year and Old Subsequence 动态dp


    Legend

    Link \(\textrm{to Codeforces}\).

    给定长度为 \(n\ (4 \le n \le 200\ 000)\) 的数字串,\(q\ (1 \le q \le 200\ 000)\) 次询问,每次询问一个连续子串 \([l,r]\),询问:

    • 使得这个连续子串内不存在子序列 \(2016\) 并存在子序列 \(2017\) 至少要删去多少个数位。

    Editorial

    考虑暴力怎么做,有个很显然的在 \(\textrm{trie}\)\(\textrm{dp}\) 的做法(这个 \(\textrm{trie}\) 的大小只有 \(5\)),即记录走到某个节点的最少删除次数。

    那么你只需要把这个 \(\textrm{dp}\) 用矩阵转移优化一下就好了。

    \[\begin{bmatrix} [s_i = 2] & \infty & \infty & \infty & \infty \\ [s_i \not= 2]\infty & [s_i = 0] & \infty & \infty & \infty \\ \infty & [s_i \not= 0]\infty & [s_i=1] &\infty & \infty \\ \infty & \infty & [s_i\not = 1]\infty & [s_i=6\or s_i=7] & \infty \\ \infty & \infty & \infty & [s_i \not= 7]\infty & [s_i=6] \\ \end{bmatrix} \times \begin{bmatrix} ST \\ a_2 \\ a_0 \\ a_1 \\ a_7 \\ \end{bmatrix} = \]

    Code

    注意矩阵的相乘顺序,所以线段树里维护的因当是从区间右侧乘到左侧的矩阵。但查询的时候依然是从左子树到右子树。

    注意到右侧乘的是一个向量,所以在查询的时候可以省一些常数,由于我太懒,没有去实现。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int INF = 1e9;
    const int MX = 2e5 + 233;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9')
    		x = x * 10 + k - '0' ,k = getchar();	
    	return x;
    }
    
    struct Matrix{
    	int A[5][5];
    	Matrix(){memset(A ,0x3f ,sizeof A);}
    	Matrix(int num){
    		memset(A ,0x3f ,sizeof A);
    		A[0][0] = (num == 2);
    		A[1][0] = (num != 2) * INF;
    		A[1][1] = (num == 0);
    		A[2][1] = (num != 0) * INF;
    		A[2][2] = (num == 1);
    		A[3][2] = (num != 1) * INF;
    		A[3][3] = (num == 6 || num == 7);
    		A[4][3] = (num != 7) * INF;
    		A[4][4] = (num == 6);
    	}
    	Matrix operator *(const Matrix &B)const{
    		Matrix C;
    		for(int i = 0 ; i < 5 ; ++i)
    			for(int j = 0 ; j < 5 ; ++j)
    				for(int k = 0 ; k < 5 ; ++k)
    					C.A[i][j] = min(C.A[i][j] ,A[i][k] + B.A[k][j]);
    		return C;
    	}
    	void output(){
    		for(int i = 0 ; i < 5 ; ++i)
    			for(int j = 0 ; j < 5 ; ++j)
    				printf("%d%c" ,A[i][j] ," \n"[j == 4]);
    		puts("||||||||||||||||||||||||||||||||||||||||||");
    	}
    };
    
    struct node{
    	int l ,r;
    	Matrix s;
    	node *lch ,*rch;
    	node(int _l ,int _r ,int num ,node *L ,node *R){
    		s = Matrix(num);
    		l = _l ,r = _r;
    		lch = L ,rch = R;
    	}
    	void pushup(){s = rch->s * lch->s;}
    }*root;
    
    node *build(int l ,int r ,int *A){
    	node *x = nullptr;
    	if(l == r) x = new node(l ,r ,A[l] ,nullptr ,nullptr);
    	else{int mid = (l + r) >> 1;
    		node *lch = build(l ,mid, A);
    		node *rch = build(mid + 1 ,r ,A);
    		x = new node(l ,r ,-1 ,lch ,rch);
    		x->pushup();
    	}return x;
    }
    
    void query(node *x ,int l ,int r ,Matrix &Ans){
    	if(l <= x->l && x->r <= r) return Ans = x->s * Ans ,void();
    	if(l <= x->lch->r) query(x->lch ,l ,r ,Ans);
    	if(r > x->lch->r) query(x->rch ,l ,r ,Ans);
    }
    
    char str[MX];
    int A[MX];
    int main(){
    	int n = read() ,q = read();
    	cin >> (str + 1);
    	for(int i = 1 ; i <= n ; ++i){
    		A[i] = str[i] - '0';
    		// printf("%d\n" ,A[i]);
    	}
    	root = build(1 ,n ,A);
    	while(q--){
    		int l = read() ,r = read();
    		Matrix Ans = Matrix();
    		Ans.A[0][4] = 0;
    		query(root ,l ,r ,Ans);
    		// Ans.output();
    		printf("%d\n" ,Ans.A[4][4] > n ? -1 : Ans.A[4][4]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    CxImage图像库的使用
    CxImage动态加载图片(判断图片文件类型)
    实现一个类似360的button
    cximage功能简介
    开源图像处理软件代码
    MFC + CxImage 实现自绘半透明按钮
    VC 下加载 JPG / JPEG / GIF / PNG 图片最简单的方法
    时序数据库InfluxDB:简介及安装
    学习springboot整合mybatis并编写测试类
    Mybatis-Plus使用全解
  • 原文地址:https://www.cnblogs.com/imakf/p/13668835.html
Copyright © 2020-2023  润新知