JZOJ6008. 【THUWC2019模拟2019.1.18】Sequence

15 篇文章 0 订阅
1 篇文章 0 订阅

题意:

茉优最近研究发现,一个人的想愿能力可以认为是字符串 S S S的一个子串 S [ l , r ] S[l,r] S[l,r],而连接值可以认为是这个子串的本质不同子序列个数。现在她想验证她的结论是否正确,于是她给了你 Q Q Q个询问,希望你帮她来计算,注意空串也是子序列。

数据范围:

Analysis:

考虑一个暴力的 D p Dp Dp,设 f i f_i fi表示以下标 i i i结尾的本质不同子序列个数,每次转移就是前面所有 f i f_i fi的和,考虑如果结尾相同会算重,于是我们把所有与 s i s_i si相同的 s j s_j sj f j f_j fj减去。这样可以得到一个 O ( Q ∣ S ∣ ) O(Q|S|) O(QS)的算法。
我们考虑多设一维状态把 f i f_i fi前缀和, f i , j f_{i,j} fi,j表示到 i i i,以字符 j j j结尾的不同子序列个数。这种区间询问动态 D p Dp Dp,一般都是转成矩阵的形式来做。
我们考虑它的转移矩阵大约是一个对角线上全为 1 1 1,且 S i S_i Si对应的一列上全为 1 1 1的形式。
并且考虑多开一行一列表示空串的方案。
没有修改,我们可以预处理从左到右乘的前缀积矩阵,从右到左乘的前缀积逆矩阵。
暴力转移是 5 2 3 52^3 523的,过不去。挖掘矩阵性质。
对于正矩阵:发现每一次转移,除了中间全是 1 1 1的一列,其他地方不变,而中间那一列则是变成每一行的和。那么我们可以动态维护每一行的和,这样一次转移复杂度就是 52 52 52了。
对于逆矩阵:同理观察,发现是每一次每一列减去某一行的一个值,我们考虑打减法标记,这样也可以动态维护过去。
我们就完成了 O ( 52 ∗ ∣ S ∣ ) O(52*|S|) O(52S)的预处理。
考虑怎么求答案?
我们需要的是: [ 0 , 0 , 0 , 0 , . . . , 1 ] ∗ B l − 1 − 1 ∗ B r [0,0,0,0,...,1]*B^{-1}_{l-1}*B_r [0,0,0,0,...,1]Bl11Br B B B是预处理的前缀积矩阵。
那么乘上逆矩阵会变成逆矩阵的最后一行,然后再乘 B r B_r Br
我们最后只需要这个行向量的每一项的和,发现乘 B r B_r Br的话,每一项都乘了对应行的和。
因此我们只要维护出每一个逆矩阵的最后一行,以及正矩阵每一行的和就行了。
复杂度 O ( ( ∣ S ∣ + Q ) ∗ 52 ) O((|S|+Q)*52) O((S+Q)52)

Code:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 1e6 + 5;
const int M = 52;
const int mo = 998244353;
typedef long long ll;
char s[N];
int las[N][M + 5],S[N][M + 5];
int tag[M + 5],H[M + 5],now[M + 5][M + 5];
int n,Q,a,b,p,q,r,ans;
inline int ts(char c) { return c >= 'a' ? c - 'a' + 26 : c - 'A'; }
int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	scanf("%s%d%d%d%d%d%d",s + 1,&Q,&a,&b,&p,&q,&r); n = strlen(s + 1);
	for (int i = 0 ; i <= M ; ++i) H[i] = now[i][i] = 1;
	for (int i = 1 ; i <= n ; ++i)
	{
		int L = ts(s[i]);
		for (int j = 0 ; j <= M ; ++j)
		{
			int c = now[j][L];
			now[j][L] = H[j],H[j] = ((ll)H[j] + now[j][L] - c + mo) % mo;
		}
		for (int j = 0 ; j <= M ; ++j) S[i][j] = H[j];
	} memset(now,0,sizeof(now));
	for (int i = 0 ; i <= M ; ++i) now[i][i] = 1;
	for (int i = 1 ; i <= n ; ++i)
	{
		int L = ts(s[i]);
		for (int j = 0 ; j <= M ; ++j)
		{
			now[L][j] = (now[L][j] + tag[j]) % mo;
			tag[j] = (tag[j] + mo - now[L][j]) % mo,now[L][j] = (now[L][j] + mo - tag[j]) % mo;
		}
		for (int j = 0 ; j <= M ; ++j) las[i][j] = (now[M][j] + tag[j]) % mo;
	}
	while (Q--)
	{
		int a0 = a,b0 = b;
		a = ((ll)p * a0 % mo + (ll)q * b0 % mo + ans + r) % mo;
		b = ((ll)p * b0 % mo + (ll)q * a0 % mo + ans + r) % mo;
		int l = min(a % n,b % n) + 1,r = max(a % n,b % n) + 1;
		if (l == 1) ans = S[r][M];
		else
		{
			ans = 0;
			for (int j = 0 ; j <= M ; ++j) ans = (ans + (ll)las[l - 1][j] * S[r][j] % mo) % mo;
		}
	} printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`sltest.testsequence.addStepBefore` 函数的用法如下: ```matlab sltest.testsequence.addStepBefore(blockPath, newStep, stepPath) ``` 其中,`blockPath` 是要添加测试步骤的 Simulink 模块路径,`newStep` 是要添加的测试步骤,`stepPath` 是要添加新步骤的位置。这个函数将在 `stepPath` 指定的位置之前添加新步骤。 例如,我们可以使用以下代码在 Simulink Test Sequence 中添加一个测试步骤: ```matlab % 打开 Simulink Test Sequence testSeq = sltest.testmanager.getTestSuites('Test Sequence'); open(testSeq); % 获取 Test Sequence 中的第一个测试用例 testCase = getTestCases(testSeq); testCase = testCase{1}; % 获取测试用例中的第一个测试序列 testSeqObj = getTestSequences(testCase); testSeqObj = testSeqObj{1}; % 获取测试序列中第一个测试步骤的路径 stepPath = getTestSteps(testSeqObj); stepPath = stepPath{1}; % 在第一个测试步骤之前添加一个新的测试步骤 newStep = sltest.testsequence.TestStep('Description', '测试步骤描述'); blockPath = '模块名称/子系统名称'; sltest.testsequence.addStepBefore(blockPath, newStep, stepPath); ``` 在这个例子中,我们首先打开 Simulink Test Sequence,并获取第一个测试用例和第一个测试序列。然后,我们获取第一个测试步骤的路径,并使用 `sltest.testsequence.TestStep` 创建一个新的测试步骤。最后,我们使用 `sltest.testsequence.addStepBefore` 将新步骤添加到第一个测试步骤之前。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值