1.题目链接。题目大意:给一个长度为1e5的字符串,定义一个字符串是好的字符当且仅当这个字符串包好9102这个子序列并且不包括8102这个子序列,现在给出q个询问,每个询问时一段区间[l,r]询问字符串[l,r]这段区间内,至少移除多少个字符使得它变成好的字符串,如果移除多少个都不行,输出-1.
2.这个题目其实还是有点意思的,其实主要的考察点就是状态机。借鉴CF 上的某道题目,定义状态。
/*
0:空集
1:2
2:20
3:201
4:2019
*/
(1)每个位置的字符都有一种状态,定义f[i][j]数组代表状态的转移。比如当前字符为2,那么当前字符的f数组只有f[1][1]这一项为0,f[1][2]代表1状态向2状态转移,也就是2-->20.因为当前只有一个字符,所以这个转移时不成立的,那么转移的代价就是无穷大。所以这样可以把所有的点给初始化掉,这样每个字符其实就有一个代表当前状态的矩阵。这个转移矩阵描述了它转移的代价。
(2)现在考虑新字符的加入,比如2后是0,那么这个时候,0的矩阵就要发生变化。因为0前边有了2,所以从2-->20,不需要花费什么代价,也就是f[1][2]=0,然后由于20-->2,这个转移就需要花费代价了,因为只能通过把0删除才能实现这一操作,所以这个代价f[1[1]=1,也就是加入0之后再想变回原来的状态是需要花费代价的。这样,我们就能够顺序的把整个字符串的每个点的矩阵给初始化了。
(3)考虑如何统计答案,因为询问的是一段区间[l,r],也就是字符串从l到r这段区间内,从状态0变到状态4的代价,如果从图论的角度考虑这个问题,把a[l]作为起点,a[r]作为终点,那么这中间的每个点都会提供一些有向的边,边权为1,问题转化为找到从l到r的最短路。这其实是一个多源最短路,因为询问很多,每次询问都是一个源点一个终点。所以需要考虑Floyd算法。那么每次询问就做一次Floyd算法?显然不行吧,这时间复杂度太高了。所以考虑预处理一些区间信息,可以把一些区间的[l,r]的最小代价存起来,然后询问的时候把当前区间给分割成这些区间的加和。好吧,其实就是线段是,用线段树维护一下这个区间转移代价最小的数组就OK了。
线段是上的每一个接待你维护的就是当前区间的最小代价的转移矩阵,也就是f数组。合并两个区间直接采用暴力的枚举中间点的形式,所以答案就是这个区间的f[0][4]。
最后需要说明的就是是按照2019和2018来做的,所以对于题目中的9102和8102,把字符串逆转一下,区间也对应着逆转一下就行。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxx = 2e5 + 100;
/*
0:空集
1:2
2:20
3:201
4:2019
*/
struct node {
int a[5][5];
node operator+(const node& b)const
{
node res;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
res.a[i][j] = inf;
for (int k = 0; k < 5; k++)
{
res.a[i][j] = min(res.a[i][j], a[i][k] + b.a[k][j]);
}
}
}
return res;
}
}p[maxx << 2];
char s[maxx], ss[maxx];
int n, m;
inline void pushup(int cur)
{
p[cur] = p[cur << 1] + p[cur << 1 | 1];
}
inline void build(int l, int r, int cur)
{
if (l == r)
{
for (int i = 0; i < 5; i++)for (int j = 0; j < 5; j++) p[cur].a[i][j] = (i == j) ? 0 : inf;
if (s[l] == '2') p[cur].a[0][0] = 1, p[cur].a[0][1] = 0;
else if (s[l] == '0') p[cur].a[1][1] = 1, p[cur].a[1][2] = 0;
else if (s[l] == '1') p[cur].a[2][2] = 1, p[cur].a[2][3] = 0;
else if (s[l] == '9') p[cur].a[3][3] = 1, p[cur].a[3][4] = 0;
else if (s[l] == '8') p[cur].a[3][3] = 1, p[cur].a[4][4] = 1;
return;
}
int mid = l + r >> 1;
build(l, mid, cur << 1);
build(mid + 1, r, cur << 1 | 1);
pushup(cur);
}
inline node query(int L, int R, int l, int r, int cur)
{
if (l <= L && R <= r) return p[cur];
int mid = L + R >> 1;
if (r <= mid) return query(L, mid, l, r, cur << 1);
else if (l > mid) return query(mid + 1, R, l, r, cur << 1 | 1);
else return query(L, mid, l, mid, cur << 1) + query(mid + 1, R, mid + 1, r, cur << 1 | 1);
}
int main()
{
int l, r;
while (~scanf("%d%d", &n, &m))
{
scanf("%s", ss + 1);
for (int i = 1; i <= n; i++) s[i] = ss[n - i + 1];
build(1, n, 1);
while (m--)
{
scanf("%d%d", &l, &r);
node ans = query(1, n, n - r + 1, n - l + 1, 1);
if (ans.a[0][4] == inf) printf("-1\n");
else printf("%d\n", ans.a[0][4]);
}
}
return 0;
}