CF750E New Year and Old Subsequence题解——dp既出,矩阵归来

Description

给定一个字符串,每个字符都是 0 − 9 0-9 09之间的数。

每次查询在一段区间 [ l , r ] [l,r] [l,r]中至少需要删掉多少个字符,才能使得剩下的字符串不包含子序列2016且包含子序列2017。

Solution

考虑 d p dp dp

d p i , j : dp_{i,j}: dpi,j: 目前匹配到了串2017的第 j j j位的最少删去字符数量

状态转移如下:

①若 a i = 2 a_i=2 ai=2
(1)不删去 a i a_i ai。此时 a i a_i ai的作用在于将一个暂未匹配的状态( ∅ ∅ )变为一个匹配 1 1 1位的状态(“2”)。在这种决策下有 d p i , 1 = d p i − 1 , 0 dp_{i,1}=dp_{i-1,0} dpi,1=dpi1,0
(2)删去 a i a_i ai。可以发现删除 a i a_i ai起到了保留状态的作用,可以不改变一个匹配 0 0 0位的状态。在这种决策下有 d p i , 0 = d p i − 1 , 0 + 1 dp_{i,0}=dp_{i-1,0}+1 dpi,0=dpi1,0+1

②若 a i = 0 a_i=0 ai=0,与①同理:
(1) d p i , 2 dp_{i,2} dpi,2的决策点包含 d p i − 1 , 1 dp_{i-1,1} dpi1,1
(2) d p i , 1 dp_{i,1} dpi,1的决策点包含 d p i − 1 , 1 + 1 dp_{i-1,1}+1 dpi1,1+1

③若 a i = 1 / 7 a_i=1/7 ai=1/7,与①②同理,请自行推导。
④若 a i = 6 a_i=6 ai=6
(1)不删去 a i a_i ai。这种转移合法当且仅当当前匹配到的位数不超过 2 2 2,否则转移不合法。为什么这里有一个状态转移的合法限制呢?假设匹配了 3 3 3位,那么此时就含有子序列 201 201 201,添上一个 6 6 6后就存在了子序列 2016 2016 2016而这时题目严格禁止出现的。匹配了 4 4 4位的情况同理。所以, d p i , 0 , d p i , 1 , d p i , 2 dp_{i,0},dp_{i,1},dp_{i,2} dpi,0,dpi,1,dpi,2可以直接转移过来,但 d p i , 3 , d p i , 4 dp_{i,3},dp_{i,4} dpi,3,dpi,4必须分别从 d p i − 1 , 3 , d p i − 1 , 4 dp_{i-1,3},dp_{i-1,4} dpi1,3,dpi1,4 1 1 1而转移过来。

⑤若 a i a_i ai为其他的数,那么不会有任何影响,直接转移过来即可。

对于每次询问,扫一遍整个区间进行转移,时间复杂度为 O ( 5 2   q n ) O(5^2\ qn) O(52 qn),无法通过。


这一类含有“区间查询”且不含区间修改的 d p dp dp是经典的动态 d p dp dp,即线段树维护矩乘的模型。

这句话说得有亿点绕?没关系,我们形象地解释一遍。

不难发现当前的状态是一个长度为 5 5 5的小数组,每经过一个位置就要经过对应的变换(转移)。这种变换可以转化为广义矩阵乘法的形式。

更具体地说,状态转移形如 d p i , j = d p i − 1 , p + c o s t ( p , j ) dp_{i,j}=dp_{i-1,p}+cost(p,j) dpi,j=dpi1,p+cost(p,j),我们可以大胆地重定义矩阵乘法为 C i , j = min ⁡ k = 1 n ( A i , k + B k , j ) C_{i,j}=\min_{k=1}^n (A_{i,k}+B_{k,j}) Ci,j=k=1minn(Ai,k+Bk,j)

显然这是有结合律的。对于每一个位置的对应状态转移可以压缩为一个矩阵,表示“当前”状态通过这个位置要乘上这个矩阵。

最后我们分析每次区间查询的本质: 等价于一次区间查询矩阵乘积。我们可以采用线段树维护区间矩乘,从而解决了本题。

时间复杂度为 O ( 5 3   n log ⁡ n ) O(5^3\ n \log n) O(53 nlogn)

Code

#include <bits/stdc++.h>
#define inf 2000000007
#define int long long
using namespace std;
const int maxl=200005;
int read(){
	int s=0,w=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-')  w=-w;ch=getchar();}
	while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
	return s*w;
} 

int n,q,le,ri;
int s[maxl];

struct Matrix{
	int a[5][5];
}tree[maxl*4],b[maxl];

Matrix operator * (const Matrix &x,const 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;
}

void pushup(int rt){tree[rt]=tree[2*rt]*tree[2*rt+1];};
void build_tree(int l,int r,int rt){
	if (l==r){
		tree[rt]=b[l];
		return;
	}
	int mid=(l+r)>>1;
	build_tree(l,mid,2*rt),build_tree(mid+1,r,2*rt+1);
	pushup(rt);
}

Matrix query(int nl,int nr,int l,int r,int rt){
	if (nl<=l&&r<=nr){
		return tree[rt];
	}
	int mid=(l+r)>>1;Matrix res;
	if (nl<=mid&&nr<=mid)  res=query(nl,nr,l,mid,2*rt);
	else if (nl>mid&&nr>mid)  res=query(nl,nr,mid+1,r,2*rt+1);
	else if (nl<=mid&&nr>mid)  res=query(nl,nr,l,mid,2*rt)*query(nl,nr,mid+1,r,2*rt+1);
	return res;
}

signed main(){
	n=read(),q=read();
	for (int i=1;i<=n;i++){
		char tmpxl;
		cin>>tmpxl;
		s[i]=tmpxl-'0';
	}
	for (int i=1;i<=n;i++){
		for (int j=0;j<5;j++){
			for (int k=0;k<5;k++){
				if (j!=k)  b[i].a[j][k]=inf;
				else b[i].a[j][k]=0;
			}
		}
		if (s[i]==2)  b[i].a[0][0]=1,b[i].a[0][1]=0;
		if (s[i]==0)  b[i].a[1][1]=1,b[i].a[1][2]=0;
		if (s[i]==1)  b[i].a[2][2]=1,b[i].a[2][3]=0;
		if (s[i]==7)  b[i].a[3][3]=1,b[i].a[3][4]=0;
		if (s[i]==6)  b[i].a[3][3]=1,b[i].a[4][4]=1;
	}
	build_tree(1,n,1);
	while (q--){
		le=read(),ri=read();
		int ans=query(le,ri,1,n,1).a[0][4];
		if (ans>ri-le+1)  puts("-1");
		else printf("%lld\n",ans);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值