题目链接:
先讲讲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]可以通过某种状态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; }
来源:51CTO
作者:nowting_csdn
链接:https://blog.csdn.net/weixin_42373330/article/details/100689137