ICode9

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

「2017 山东一轮集训 Day6」子序列

2022-07-13 20:03:17  阅读:149  来源: 互联网

标签:std 10 Day6 int freopen 2017 mod 集训 define


复盘 \(\color{black}{\text{c}}\color{red}{\text{yx}}\) 讲的题,我是不会告诉你我不知道他网名的。

这可以来一手反复鞭尸(

Description

区间本质不同子序列,母串长度 \(n\) ,询问 \(q\) 次,字符集大小 \(|\sum|\) 。

\(n,\ q\leq 10 ^ 5,\ |\sum| \leq 9\)

Analysis

本来还有一个区间本质不同子串,但显然这俩都不是一个类型的(

显然子序列和子串都不是一个量级的,肯定又要用到压缩状态的好手:

DP。

我们可以考虑只枚举最后一位是什么,我们可以考虑在前面 \(i\) 个里面任选,结尾是 \(j\) 的数量,令为 \(f_{i, j}\) ,同时令结尾 \(m+1\) 的地方什么都没有填。

所以对于结尾不是 \(j\) 的地方,都只能继承上一个位置的答案,反之,则任何位置都能转移下来。

而且这不会算重:

假如存在形如 “STS” 的形式,当后面的 “S” 完成了拼接的之后,前面的 “S” 可以视作拼接成了 “S__S” 的样子,并没有重,反而正好不漏。

DP 转移如下:

\[ f_{i, j} = \left\{ \begin{array}{ll} f_{i - 1, j}&,j \neq S_i \\ \sum_{k = 1} ^ {m + 1} f_{i - 1, k}&,j = S_i \end{array} \right. \]

最终答案就是:\(\sum_{k = 1} ^ m f_{n, k}\) 。

那我们就得到了 \(O(qnm)\) 的憨憨做法。

sol0

/*
 
*/
#include 
using namespace std;

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define Check(a) freopen(a".in", "r", stdin), freopen(a".ans", "w", stdout);

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int=""> pii;
#define fi first
#define se second
#define mp std::make_pair

const int mod = 1e9 + 7;
template 
inline int M(A x) {return x;}
template 
inline int M(A x, B ... args) {return 1ll * x * M(args...) % mod;}

#define mi(x) (x >= mod) && (x -= mod)
#define ad(x) (x < 0) && (x += mod)

const int N = 1e5 + 10;

int n, q, f[N][10], su[N][10];
char s[N];

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	std::cin >> (s + 1) >> q; n = strlen(s + 1);

	for (int i = 1, l, r; i <= q; ++i) {
		std::cin >> l >> r;

		for (int k = 0; k < 9; ++k) {
			f[l - 1][k] = su[l - 1][k] = 0;
		}
		f[l - 1][9] = su[l - 1][9] = 1;
		for (int j = l; j <= r; ++j) {
			for (int k = 0; k < 10; ++k) {
				if (k == s[j] - 'a') {
					f[j][k] = su[j - 1][9];
				}
				else f[j][k] = f[j - 1][k];

				if (!k) su[j][k] = f[j][k];
				else su[j][k] = su[j][k - 1] + f[j][k];
				mi(su[j][k]);
			}
		}

		std::cout << su[r][9] - 1 << "\n";
	}

	return 0;
}


Sol1

发现转移比较单一,考虑能不能矩阵预处理一下。

因为不等于是对应位直接转移,等于至整体转移,所以就是对角线全是 1 以及其中一行全是 1 :

\[A_i = \left( \begin{array}{l} 1\ \ 0\ \ 0\ \ 0\ \ 0 \\ 1\ \ 1\ \ 1\ \ 1\ \ 1 \\ 0\ \ 0\ \ 1\ \ 0\ \ 0 \\ 0\ \ 0\ \ 0\ \ 1\ \ 0 \\ 0\ \ 0\ \ 0\ \ 0\ \ 1 \end{array} \right) \]

这样的话答案就会形如一段 \(A\) 的乘积,因为矩阵乘法没有交换律,所以除了前缀积,还要维护后缀积(逆矩阵)。

那无论是预处理还是查询就都是 \(O(m ^ 3)\) 的了。总体下来就是 \(O\Big((n + q) m ^ 3\Big)\) 。

Sol1

/*
 
*/
#include 
using namespace std;

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define Check(a) freopen(a".in", "r", stdin), freopen(a".ans", "w", stdout);

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int=""> pii;
#define fi first
#define se second
#define mp std::make_pair

const int mod = 1e9 + 7;
template 
inline int M(A x) {return x;}
template 
inline int M(A x, B ... args) {return 1ll * x * M(args...) % mod;}

#define mi(x) (x >= mod) && (x -= mod)
#define ad(x) (x < 0) && (x += mod)

const int N = 1e5 + 10;

int n, q;
char s[N];

struct matrix {
	int a[10][10];
	matrix() {memset(a, 0, sizeof(a));}

	inline void init() {for (int i = 0; i < 10; ++i) a[i][i] = 1;}

	matrix operator * (const matrix &it) const {
		matrix t;
		for (int i = 0; i < 10; ++i) {
			for (int k = 0; k < 10; ++k) {
				if (!a[i][k]) continue;
				for (int j = 0; j < 10; ++j) {
					t.a[i][j] += M(a[i][k], it.a[k][j]);
					mi(t.a[i][j]);
				}
			}
		}
		return t;
	}
} pre[N], suf[N];

inline void init() {
	pre[0].init(); suf[0].init();

	for (int i = 1, c; i <= n; ++i) {
		c = s[i] - 'a';
		
		for (int j = 0; j < 10; ++j) {
			suf[i].a[c][j] = 1;
			pre[i].a[c][j] = mod - 1;
		}

		pre[i].init(); suf[i].init();
	}

	for (int i = 2; i <= n; ++i) {
		pre[i] = pre[i - 1] * pre[i];
		suf[i] = suf[i] * suf[i - 1];
	}
}

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	std::cin >> (s + 1) >> q; n = strlen(s + 1);
	init();
	for (int i = 1, l, r; i <= q; ++i) {
		std::cin >> l >> r;

		matrix res = suf[r] * pre[l - 1];
		int ans = 0;
		for (int j = 0; j < 10; ++j) {
			ans += res.a[j][9]; mi(ans);
		}

		std::cout << ans - 1 << "\n";
	}

	return 0;
}


Sol2

一下子写了太多,先咕咕咕啦(

标签:std,10,Day6,int,freopen,2017,mod,集训,define
来源: https://www.cnblogs.com/Illusory-dimes/p/16475339.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

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

ICode9版权所有