专栏文章

题解:AT_abc433_f [ABC433F] 1122 Subsequence 2

AT_abc433_f题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min1byej
此快照首次捕获于
2025/12/01 18:57
3 个月前
此快照最后确认于
2025/12/01 18:57
3 个月前
查看原文

1122 Subsequence 2

题目大意

给定一个由数字组成的字符串 SS\\ 现给出对“11221122 字符串”的定义:对于某字符串 TT,当其满足以下条件时,就被成为“11221122 字符串”。
  • TT 是一个由数字组成的非空字符串。
  • T|T|(字符串 TT 的长度) 为偶数。
  • 字符串 TT 中第 11 个字符至第 T2\frac{|T|}2 个字符均为同一数字。 字符串 TT 中从第 T2+1\frac{|T|}2+1 个字符到第 T|T| 个字符均为 TT 的第 11 个字符(表示的)数字加 11 形成的字符。
求字符串 SS 中所有(不一定是连续的)子序列中为“11221122 字符串”的数量,结果对 998244353998244353 取模。\\ 若两个子序列来自不同位置,则即使其字符串内容相同,也应分别计数。

思路分析

考虑枚举 SS 中满足条件的子序列的左半段最后一个字符的位置(第 T2\frac{|T|}2 个字符)\\ 假设这是知道的唯一条件,即设该位置字符必选,考虑在这种情况下计算符合条件的子序列数量。
假设当前答案子序列的左半段最后一个字符的位置(在原字符串中)为 ii,则该字符为 SiS_i\\ii 的左侧(包含 ii)与 SiS_i 相同的字符有 aa 个,ii 的右侧与 Si+1S_i+1 相同的字符有 bb 个,在 ii 的左、右侧都会分别选取 ll 个字符(则有 {la1b\begin{cases} l \le a \\ 1 \le b \\ \end{cases} ,换句话说,就是 lmin(a,b)l \le \min(a,b))。\\ 因此,ii 的左侧(包含 ii)需要在 a1a-1(因为第 ii 个字符必选)个字符中选 ll 个,ii 的右侧需要在 bb 个字符中选 ll 个,因此总方案就有 Ca1l1CblC_{a-1}^{l-1} \cdot C_b^l 种。
则以 SiS_i 为答案左半段最后一个字符时,对总数的贡献为:\\
l=1min(a,b)Ca1l1Cbl=l=1min(a,b)Ca1alCbl=Ca+b1a\begin{aligned} \sum_{l=1}^{\min(a,b)}C_{a-1}^{l-1} \cdot C_b^l &=\sum_{l=1}^{\min(a,b)}C_{a-1}^{a-l} \cdot C_b^l\\ &=C_{a+b-1}^a \end{aligned}
大家看着这个转移可能会很迷惑,因此在此做出解释:
  • 第一步只变了一处,依靠的是 Cxy=CxyxC_x^y=C_x^{y-x}
  • 第二步注意到式子被表现成了 i=0kCmiCnki\sum_{i=0}^{k} C_m^i \cdot C_n^{k-i} 的形式,因此根据 范德蒙恒等式,可以化为 Cm+nkC_{m+n}^k 的形式。
最后就很简单了,逆元预处理阶乘后借助公式求解组合数即可。

代码详解

CPP
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
string s;
unordered_map<char,int> frt,bck; //当前字符在其位置前、后出现的次数
int n,fac[2000005]={1},inv[2000005],ans;
int pows(int a,int b,int p){
	int now=1;
	while(b>0){
		if(b&1) now=now*a%p;
		a=a*a%p;
		b>>=1;
	}
	return now;
}
//利用预处理数组计算组合数
int cxy(int x,int y){
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>s;
	n=s.size();
	s=" "+s;
    //逆元预处理
	for(int i=1;i<=2000000;i++) fac[i]=fac[i-1]*i%mod;
	inv[2000000]=pows(fac[2000000],mod-2,mod);
	for(int i=1999999;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
    //先假设所有字符都在答案后半段
	for(int i=1;i<=n;i++) bck[s[i]]++;
    //O(|S|)计算答案
	for(int i=1;i<=n;i++){
		frt[s[i]]++,bck[s[i]]--;
		ans=(ans+cxy(frt[s[i]]+bck[s[i]+1]-1,frt[s[i]]))%mod; //公式解释见上文
	}
	cout<<ans;
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...