2019南昌icpc网络赛C.hello 2019 & codeforce 750E.New Year and Old Subsequence(线段树+矩阵+dp思想)*

匿名 (未验证) 提交于 2019-12-03 00:01:01

题目链接:

C.hello 2019
cf 750E


先讲讲cf 750E,
首先安利下cf这场比赛的题解http://codeforces.com/blog/entry/49412
关于这题核心就在题解链接中的讨论部分,当发现类似连在一起几把锁的图案就是该部分
虽然是英文的,但讲的很好(可以像我一样用划词翻译
下面理了理这题的思路


最难的还是如何想到构造这样一个矩阵
(没办法,自己都是看题解的。这个得看实力,做过可能下次就会了)
这里用数字0~4分别表示5种状态

1:=2
2:=20
3:=201
4:=2017
因此用5*5的矩阵a来存储这5种状态
a[i][j]表示状态i到状态j最小代价(即最少需要删除的字符)
n个位置每个位置都有转换矩阵,共有n个矩阵


每个矩阵的作用就是:
当遍历到第x个字符时,已有预处理得到x位置上的转换矩阵tr[x].a,用矩阵A表示某区间[st,x-1]的状态矩阵,那么如何得到区间[st,x]的状态矩阵(设为矩阵B)呢?
矩阵B可以通过矩阵A与转换矩阵tr[x].a运算得来,这就是转换矩阵的作用
如何运算?
讲讲这里的矩阵是如何运算的,也就是如何由A和tr[x].a得出B:
:A矩阵中A[i][j]表示的是区间[st,x-1]内从状态i->j所花费的最小代价
B[i][j]=min(A[i][k]+tr[x].a[k][j]),    k=[0,5)B[i][j]=min(A[i][k]+tr[x].a[k][j]) ,\; \; k=[0,5)
解释:因为题目所求为最小,B[i][j]可以通过某种状态k为中介,然后取其和的最小值

而tr[x].a 的a[i][j]相当于在区间[x,x]中从状态i->j所花费的最小代价
换句话说:在原先的状态上,加上x位置上的字符,能否变成0,1,2,3,4的状态,若能,代价是多少?然后将这些数据都存放在转换矩阵tr[x].a中

链接中的那个5把锁状的图可以很好理解不同状态间的转换
如何根据每个位置的字符来确定转换矩阵?

对于转换矩阵先初始为:除正对角线上为0,其余都为inf
:因为初始的时候状态i只能转换到状态i不需要删字符,转换到其他状态都是不可达的
遍历到第x个字符时s[x],对s[x]的情况进行讨论:
s[x]=‘2’: 字符2字符只会影响状态0→状态1的过程,因此由状态0->状态1不需要删字符,即a[0 ][1]=0,但由״̬0->״̬0,已经被破坏,因此为了维持状态0->状态0,需要将字符2删除的结果存放在转换矩阵中,即a[0][0]=1;而剩下的状态都不能加上字符2变成其余状态,故都为inf
s[x]=‘0’:字符0会影响 ״̬1->״̬2过程,字符0,1,7道理都一样,就不重复了
s[x]=‘1’
s[x]=‘7’
s[x]=‘6’:对于字符6,由于不能包含2016,因此对于维持 状态3(‘201’)->状态3(‘201’),需删掉6,故a[3][3]=1;对于状态4(‘2017’),即若不删6,则会包含2016,因此要删去,即a[4][4]=1
其余情况不做处理

转换矩阵tr[x].a,不管x的前面(可以看作矩阵A)是否存在某种状态,以x上 的字符是’6’为例,结合矩阵的运算,不管前面是否存在状态3或4,转换矩阵都是独立
如果不理解的话,可以拿出几个矩阵模拟一下
比如前两个字符为两个6,运算后得到为全为inf的矩阵,然后与s[x]=6的矩阵运算,结果还是inf
又例如第一个字符为2,第二个位0,运算后得到:除了A[0][1]=1,A[0][2]=0,其余都为inf 。

cf 750E代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include<iostream> #include <algorithm> using namespace std; const int maxn = 200010; const int inf = 2*maxn;  struct mat { 	int a[6][6]; 	 	void init(int x)  //初始化 	{ 		for(int i=0;i<5;i++) 			for(int j=0;j<5;j++) 				a[i][j]= (i==j)? x : inf; 	} 	mat operator + (const mat b) const 	{  		mat c; 		for(int i=0;i<5;i++) 			for(int j=0;j<5;j++) 			{ 				c.a[i][j]=inf; 				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; 	} }tr[maxn<<2]; // char tm[maxn]; char s[maxn];  void build(int l,int r,int rt) { 	 	if(l==r) 	{ 		tr[rt].init(0); 		switch(s[l]) 		{ 			case '2': tr[rt].a[0][0]=1;tr[rt].a[0][1]=0;break; 			case '0': tr[rt].a[1][1]=1;tr[rt].a[1][2]=0;break; 			case '1': tr[rt].a[2][2]=1;tr[rt].a[2][3]=0;break; 			case '7': tr[rt].a[3][3]=1;tr[rt].a[3][4]=0;break; 			case '6': tr[rt].a[3][3]=1;tr[rt].a[4][4]=1;break; 			default : break; 		} 		return ; 	} 	int mid=l+r>>1; 	build(l,mid,rt<<1); 	build(mid+1,r,rt<<1|1); 	tr[rt]=tr[rt<<1]+tr[rt<<1|1];//更新rt覆盖的区间【tr[rt].l~tr[rt].r】中的状态矩阵 	 }    mat query(int L,int R,int l,int r,int rt) { 	if(L>=l&& R<=r) 		return tr[rt]; 	int mid=L+R>>1; 	if(r<=mid) return query(L,mid,l,r,rt<<1); 	else if(l>mid) return query(mid+1,R,l,r,rt<<1|1); 	else return query(L,mid,l,r,rt<<1)+ query(mid+1,R,l,r,rt<<1|1); }  int main() { 	int n,q; 	int l,r; 	scanf("%d%d%s",&n,&q,s+1); 	build(1,n,1);  	while(q--) 	{ 		scanf("%d%d",&l,&r); 		int ans =query(1,n,l,r,1).a[0][4]; 		printf("%d\n",ans>=n?-1:ans); 	} 	 	return 0; } 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!