Codeforces E. New Year and Old Subsequence (线段树 + dp + 矩阵)

time limit per test

3 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

A string t is called nice if a string "2017" occurs in t as a subsequence but a string "2016" doesn't occur in t as a subsequence. For example, strings "203434107" and "9220617" are nice, while strings "20016", "1234" and "20167" aren't nice.

The ugliness of a string is the minimum possible number of characters to remove, in order to obtain a nice string. If it's impossible to make a string nice by removing characters, its ugliness is  - 1.

Limak has a string s of length n, with characters indexed 1 through n. He asks you q queries. In the i-th query you should compute and print the ugliness of a substring (continuous subsequence) of s starting at the index ai and ending at the index bi (inclusive).

Input

The first line of the input contains two integers n and q (4 ≤ n ≤ 200 000, 1 ≤ q ≤ 200 000) — the length of the string sand the number of queries respectively.

The second line contains a string s of length n. Every character is one of digits '0'–'9'.

The i-th of next q lines contains two integers ai and bi (1 ≤ ai ≤ bi ≤ n), describing a substring in the i-th query.

Output

For each query print the ugliness of the given substring.

Examples

input

Copy

8 3
20166766
1 8
1 7
2 8

output

Copy

4
3
-1

input

Copy

15 5
012016662091670
3 4
1 14
4 15
1 13
10 15

output

Copy

-1
2
1
-1
-1

input

Copy

4 2
1234
2 4
1 2

output

Copy

-1
-1

Note

In the first sample:

  • In the first query, ugliness("20166766") = 4 because all four sixes must be removed.
  • In the second query, ugliness("2016676") = 3 because all three sixes must be removed.
  • In the third query, ugliness("0166766") =  - 1 because it's impossible to remove some digits to get a nice string.

In the second sample:

  • In the second query, ugliness("01201666209167") = 2. It's optimal to remove the first digit '2' and the last digit '6', what gives a string "010166620917", which is nice.
  • In the third query, ugliness("016662091670") = 1. It's optimal to remove the last digit '6', what gives a nice string "01666209170".

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

这段序列, 只含有2017, 不含有2016

d[i][j]为第i个位置匹配到"?2017"(?指除2, 0, 1, 7, 6外的其他数字)的第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}

当前字符为'7'时

\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}

当前字符为'6'时

\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即可

代码:

#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 == '7')
		res.m[4][4] = 1, res.m[4][5] = 0;
	if (s == '6')
		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);
	build(1, 1, n);
	while (m--)
	{
		int ql, qr;
		scanf("%d%d", &ql, &qr);
		M ans = query(1, 1, n, ql, qr);
		printf("%d\n", ans.m[1][5] == INF ? -1 : ans.m[1][5]);
	}
	return TIME;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值