【动态规划】【动态 DP】 CF750E New Year and Old Subsequence

题目描述

定义数字串是好的当且仅当其包含子序列 2017 ,不包含子序列 2016。

定义数字串的丑陋值为最少删掉几个字符,它才能是好的,如果一直不能,就是 1

给定数字串 t ,长度为 nq 次询问求 [l,r] 的丑陋值。

1n,q2×105

算法描述

对于这种问题,我们先探究如果给你一个数字串,怎样求丑陋值。

我们发现由于是子序列,情况太复杂,没办法讨论,考虑 dp。每个点的状态定义为在其之前凑出关键字串的 “进度”,fi,0/1/2/3/4 表示前 i 个字符,凑出 /2/20/201/2017 且没有 2016 的最少删除个数,可以分讨:

fi,0=fi1,0+[si=2]

fi,1=min(fi1,0×[si=2],fi1,1+[si=0])

注意这里的 × 不是乘法,而是如果 si=2 就有这一项,不等于这一项就是 inf。

fi,2=min(fi1,1×[si=0],fi1,2+[i=1])

fi,3=min(fi1,2×[si=1],fi1,3+[si=7|si=6])

fi,4=min(fi1,3×[si=7],fi1,4+[si=6])

这样可以 Θ(n) 求一个序列的丑陋值。

那么怎样回答区间呢?

考虑用 DDP 的思路转化成矩阵,再套上线段树。这里采用 min,+ 的广义矩阵,将 [fi,0,fi,1,fi,2,fi,3,fi,4] 作为一个矩阵,手模可以得到一个 5×5 的转移矩阵:

[[si=2]0×[si=2]infinfinfinf[si=0]0×[si=0]infinfinfinf[si=1]0×[si=1]infinfinfinf[si=7|si=6]0×[si=7]infinfinfinf[si=6]]

线段树上每个点初始化,区间询问将其乘起来,再左乘上初始矩阵 [0,inf,inf,inf,inf] 即可。

时间复杂度 Θ(k3nlogn)

这题可以说是最简单的 DDP 转化,由此看出如果涉及修改的话直接单点改某一个矩阵即可,复杂度 Θ(k3logn) 的,十分优秀。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5,inf = 0x3f3f3f3f;
struct Matrix{
	int a[5][5];
};
Matrix operator *(Matrix x,Matrix y)
{
	Matrix z;
	for(int i = 0;i < 5;i++)
		for(int j = 0;j < 5;j++)
		{
			z.a[i][j] = inf;
			for(int k = 0;k < 5;k++)
				z.a[i][j] = min(z.a[i][j],x.a[i][k] + y.a[k][j]);
		}
	return z;
}
int n,q;
char s[N];
struct Segment_Tree{
	Matrix a[N << 2];
	inline void pushup(int pos) {a[pos] = a[pos << 1] * a[pos << 1 | 1];}
	inline void build(int l,int r,int pos)
	{
		if(l == r) 
		{
			for(int i = 0;i < 5;i++)
				for(int j = 0;j < 5;j++)
				{
					if(i == 0 && j == 0) a[pos].a[i][j] = (s[l] == '2');
					else if(i == 1 && j == 1) a[pos].a[i][j] = (s[l] == '0');
					else if(i == 2 && j == 2) a[pos].a[i][j] = (s[l] == '1');
					else if(i == 3 && j == 3) a[pos].a[i][j] = (s[l] == '7') | (s[l] == '6');
					else if(i == 4 && j == 4) a[pos].a[i][j] = (s[l] == '6');
					else if(i == 0 && j == 1) a[pos].a[i][j] = ((s[l] == '2') ? 0 : inf);
					else if(i == 1 && j == 2) a[pos].a[i][j] = ((s[l] == '0') ? 0 : inf);
					else if(i == 2 && j == 3) a[pos].a[i][j] = ((s[l] == '1') ? 0 : inf);
					else if(i == 3 && j == 4) a[pos].a[i][j] = ((s[l] == '7') ? 0 : inf);
					else a[pos].a[i][j] = inf;
				}
			return;
		}
		int mid = (l + r) >> 1;
		build(l,mid,pos << 1); build(mid + 1,r,pos << 1 | 1);
		pushup(pos);
	}
	inline Matrix query(int l,int r,int L,int R,int pos)
	{
		if(L <= l && r <= R) return a[pos];
		int mid = (l + r) >> 1;
		if(L <= mid && R > mid) return query(l,mid,L,R,pos << 1) * query(mid + 1,r,L,R,pos << 1 | 1);
		else if(L <= mid) return query(l,mid,L,R,pos << 1);
		else if(R > mid) return query(mid + 1,r,L,R,pos << 1 | 1);
	} 
}t;
int main()
{
	cin>>n>>q;
	scanf("%s",s + 1);
	t.build(1,n,1);
	for(int i = 1,l,r;i <= q;i++)
	{
		cin>>l>>r;
		Matrix now = t.query(1,n,l,r,1);
		int ori[5] = {0,inf,inf,inf,inf},res[5] = {inf,inf,inf,inf,inf};
		for(int j = 0;j < 5;j++)
			for(int k = 0;k < 5;k++)
				res[j] = min(res[j],ori[k] + now.a[k][j]);
		printf("%d\n",(res[4] == inf) ? -1 : res[4]);
	}
	return 0;
}
posted @   The_Last_Candy  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示