「解题报告」CF1178B WOW Factor

¿


题目链接

这是一道非常富有启发性的题目,值得一做,闪耀着人类和机器人的智慧光辉的绝佳题目 .

首先注意到 (vv)o(vv) 的结构,可以考虑枚举中间的 o,这样只需要算两边的选法然后用乘法原理即可 .

乘法原理:做一件事,完成它需要分成 \(n\) 个步骤,做第一步有 \(m_1\) 种不同的方法,做第二步有 \(m_2\) 种不同的方法,……,做第 \(n\) 步有 \(m_n\) 种不同的方法。那么完成这件事共有 \(N=m_1\times m_2\times m_3\times\cdots\times m_n\) 种不同的方法 .

根据乘法原理,其实就是选择这个字符串有三个步骤,先选左边有 \(a\) 种,中间的 o\(1\) 种,右边有 \(b\) 种,那么总方案数就是 \(N=a\times 1\times b\) .

那么考虑怎么求 \(a\)\(b\),分别考虑:

Part 1\(a\)

厉害的一步到了,令 \(f_i\) 表示 \([s_i=s_{i+1}=\texttt v]\)(这里的 \([]\) 叫 Iverson Bracket,大佬教的,\([x]\) 就是 bool(x)

那么如果 o 的位置是 \(i\),那么 \(a=f_1+f_2+\cdots+f_{i-1}\) 就是连续 vv 的个数了,不用考虑冲突,因为只需要选一个不选多个 .

如果每次都直接求和,需要约 \(10^6\times 10^6\) 次加法,无法接受,怎么办呢?

发现是要对于每个位置求,于是假设第 \(i\) 个位置的答案是 \(A\),第 \(i+1\) 个位置的答案是 \(B\)(不需要考虑是否为 o,后面说原因),那么

\[\begin{aligned}&A=f_1+f_2+\cdots+f_{i-1}\\&B=f_1+f_2+\cdots+f_{i-1}+f_i\end{aligned} \]

惊奇的发现:\(B=A+f_i\)

这样先算出第一个位置的答案,然后用一个 for 循环就可以求出每个位置的 \(a\) 值,使用时只计算有 o 的位置就行了 .

Part 2\(b\)

也是类似的做法,如果 o 的位置是 \(i\),那么 \(b=f_i+f_{i+1}+\cdots +f_n\) 就是连续 vv 的个数 .

假设第 \(i\) 个位置的答案是 \(A\),第 \(i+1\) 个位置的答案是 \(B\),那么

\[\begin{aligned}&A=f_i+f_{i+1}+f_{i+2}+\cdots+f_n\\&B=f_{i+1}+f_{i+2}+\cdots+f_n\end{aligned} \]

这时候我们得到的是 \(B=A-f_i\),也是先算出第一个位置的答案用一个 for 循环就可以求出每个位置的 \(b\) 值了 .

还可以看 \(A=B+f_i\),这样从后往前 for,初始的位置就不需要再用一个 for 循环了 .

总共的时间复杂度为 \(O(n)\),可以 Accepted 了 . 记得开 long long

接下来就是简洁的代码,细节很多,要好好考虑:

const int N = 1919810;
string s;
int n, pre[N], suf[N];
int main()
{
	cin >> s; n = s.length(); s = "$" + s; 
	for (int i=1; i<n; i++) pre[i] = pre[i-1] + ((s[i] == 'v') && (s[i+1] == 'v'));
	for (int i=n-1; i>=1; i--) suf[i] = suf[i+1] + ((s[i] == 'v') && (s[i+1] == 'v'));
	ll ans = 0;
	for (int i=1; i<n; i++)
		if (s[i] == 'o') ans += 1ll * pre[i-1] * suf[i];
	printf("%lld\n", ans);
	return 0;
}

可以看出题面的表述相当自然易懂,而每一步的思维却都非常跳跃,思维量很大结果却很简洁,推导答案的形式不涉及很困难的算法,却又不仅限于结论,而是巧妙地结合了 OI 知识,启发选手独自研发题目所需要的算法,为良心出题人点赞!

posted @ 2023-03-07 20:01  Jijidawang  阅读(47)  评论(0编辑  收藏  举报
😅​