The 2019 Asia Nanchang First Round Online Programming Contest C. Hello 2019 (线段树 + dp + 矩阵)

A digital string is "good": when it contains a subsequence 91029102 and does not contain a subsequence 81028102.

The bad value of a string is defined as how many characters are to remove at least, so that the string satisfies the "good" property. Output -1 if the string cannot satisfy the "good" property by removing some characters (0 or maybe more).

Input

The first line contains two integers n, Qn,Q(1\leq n,Q\leq2*10^5)(1≤n,Q≤2∗105). Where nn is the length of the string and QQ is the number of queries.

The second line contains a string ss that consists entirely of decimal numbers.

The next QQ line, each line contains two integers l, rl,r(1\leq l\leq r\leq n)(1≤l≤r≤n), denoting a query.

Output

For each query, output an answer which is the bad value of the substring s_ls_{l+1} \cdots s_rsl​sl+1​⋯sr​ from ss.

样例输入复制

8 3
88988102
1 8
2 8
1 7

样例输出复制

4
3
-1

题意给出长度为n的串S, 以及m次询问, 每次询问给出l, r。 求在S[l,r]中最少删除多少个字符满足

这段序列, 只含有9102, 不含有8102

这题跟CF New Year and Old Subsequence 这题一样, 除了所给字符串不同的字符一个在尾, 一个在头

对于第一个字符不同, 状态可能很多。。 弱鸡不会, 还是反过来按最后一个字符不同来处理。

问题转化成求一个区间内只含有2019, 不含有2018, 最少删除多少个字符。

思路:

d[i][j]为第i个位置匹配到"?2019"(?指除2, 0, 1, 9, 8外的其他数字)的第j个字符需要的最小代价

对于d[i][j], 从d[i - 1][k] 转移,

k = j 若当前字符添加, 会导致状态转移到j + 1, 保持状态需要删除当前字符,  代价为1, 否则代价为0。

k < j, 且当前添加当前字符到不了当前状态, 则代价为INF, 否则代价0

k > j, 从后面的状态转回去, 显然代价为INF

可以使用矩阵转移, 将原本的乘法操作, 改为加法, 累加改成取min

下面表示的矩阵中用x = INF

当前字符为'2'时

\left\{\begin{matrix} d[i][1] = d[i - 1][1] + 1\hfill\\ d[i][2] = d[i - 1][2]\hfill \end{matrix}\right.

\begin{bmatrix} 1 &0 & x& x &x \\ x& 0 & x & x &x \\ x & x & 0 & x &x \\ x& x & x& 0& x\\ x & x& x& x& 0 \end{bmatrix}

当前字符为'0'时

\left\{\begin{matrix} d[i][2] = d[i - 1][2] + 1\hfill\\ d[i][3] = d[i - 1][3]\hfill \end{matrix}\right.

\begin{bmatrix} 0 &x &x &x &x \\ x& 1 & 0 & x &x \\ x& x & 0 & x &x \\ x& x& x& 0& x\\ x& x &x &x &0 \end{bmatrix}

当前字符为'1'时

\left\{\begin{matrix} d[i][3] = d[i - 1][3] + 1\hfill\\ d[i][4] = d[i - 1][4]\hfill \end{matrix}\right.

\begin{bmatrix} 0 &x & x & x &x \\ x& 0& x& x& x\\ x& x &1 &0 &x \\ x& x& x& 0& x\\ x& x &x &x &0 \end{bmatrix}

当前字符为'9'时

\left\{\begin{matrix} d[i][4] = d[i - 1][4] + 1\hfill\\ d[i][5] = d[i - 1][5]\hfill \end{matrix}\right.

\begin{bmatrix} 0 & x & x & x &x \\ x& 0& x& x& x\\ x& x & 0 & x & x\\ x& x& x& 1 & 0\\ x &x & x & x & 0 \end{bmatrix}

当前字符为'8'时

\left\{\begin{matrix} d[i][4] = d[i - 1][4] + 1\hfill\\ d[i][5] = d[i - 1][5] + 1\hfill \end{matrix}\right.

\begin{bmatrix} 0 & x & x & x &x \\ x& 0& x& x& x\\ x& x &0 & x &x \\ x& x& x& 1& x\\ x& x & x & x &1 \end{bmatrix}

初始矩阵为

\begin{bmatrix} 0 & x & x & x &x \end{bmatrix}

我们只需要把转移矩阵相乘得到A, 再将初始矩阵乘以A即是答案

从状态1转到5, 因为取min, 所以答案即是A[1][5]

如何得到A? 使用线段树,  每个节点存一个矩阵。 查询时候区间查询l, r即可(别忘了, l, r也要反一下)

代码:

#include <bits/stdc++.h>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

#ifdef LOCAL
#define debug(x) cout << "[" __FUNCTION__ ": " #x " = " << (x) << "]\n"
#define TIME cout << "RuningTime: " << clock() << "ms\n", 0
#else
#define TIME 0
#endif
#define hash_ 1000000009
#define Continue(x) { x; continue; }
#define Break(x) { x; break; }
const int mod = 998244353;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
ll fpow(ll a, ll b, int mod) { ll res = 1; for (; b > 0; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; }
char s[N];
struct M
{
	int m[6][6];
	M()
	{
		memset(m, 0x3f, sizeof m);
		for (int i = 1; i <= 5; i++)
			m[i][i] = 0;
	}
}c[N << 2];
M add(M a, M b)
{
	M ans;
	for (int i = 1; i <= 5; i++)
		for (int j = 1; j <= 5; j++)
		{
			ans.m[i][j] = INF;
			for (int k = 1; k <= 5; k++)
				ans.m[i][j] = min(ans.m[i][j], a.m[i][k] + b.m[k][j]);
		}
	return ans;
}
M get_M(char s)
{
	M res;
	if (s == '2')
		res.m[1][1] = 1, res.m[1][2] = 0;
	if (s == '0')
		res.m[2][2] = 1, res.m[2][3] = 0;
	if (s == '1')
		res.m[3][3] = 1, res.m[3][4] = 0;
	if (s == '9')
		res.m[4][4] = 1, res.m[4][5] = 0;
	if (s == '8')
		res.m[5][5] = 1, res.m[4][4] = 1;
	return res;
}
#define ls ((x) << 1)
#define rs ((x) << 1 | 1)
#define mid ((L) + (R) >> 1)
void push_up(int x)
{
	c[x] = add(c[ls], c[rs]);
}
void build(int x, int L, int R)
{
	if (L == R)
	{
		c[x] = get_M(s[L]);
		return;
	}
	build(ls, L, mid);
	build(rs, mid + 1, R);
	push_up(x);
}
M query(int x, int L, int R, int ql, int qr)
{
	if (ql <= L && qr >= R)
		return c[x];
	M u, v;
	if (ql <= mid)
		u = query(ls, L, mid, ql, qr);
	if (qr > mid)
		v = query(rs, mid + 1, R, ql, qr);
	return add(u, v);
}
int main()
{
#ifdef LOCAL
	freopen("D:/input.txt", "r", stdin);
#endif
	int n, m;
	cin >> n >> m;
	scanf("%s", s + 1);
	reverse(s + 1, s + n + 1);
	build(1, 1, n);
	while (m--)
	{
		int ql, qr;
		scanf("%d%d", &ql, &qr);
		int u = ql;
		ql = n - qr + 1;
		qr = n - u + 1;
		M ans = query(1, 1, n, ql, qr);
		printf("%d\n", ans.m[1][5] == INF ? -1 : ans.m[1][5]);
	}
	return TIME;
}

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值