ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2019-09-10 14:38:41  阅读:128  来源: 互联网

标签:750E Old 字符 状态 矩阵 tr 2019 转换 inf



题目链接:

C.hello 2019
cf 750E


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


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



标签:750E,Old,字符,状态,矩阵,tr,2019,转换,inf

专注分享技术,共同学习,共同进步。侵权联系[admin#icode9.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有