ABC268G 题解

前言

题目传送门!

更好的阅读体验?

很牛逼的题目,这题是要从定义出发,而非 DP,但是想到这一点不简单(我太菜了)。

思路

考虑两个名字 \(s\)\(t\)

  • 如果 \(s\)\(t\) 的前缀,根据字典序的规则,\(t\) 必然比 \(s\) 靠前。即 \(0\)
  • 如果 \(t\)\(s\) 的前缀,同理,\(s\)\(t\) 靠前。即 \(1\)
  • 否则,对于任意一个位置,\(s\)\(t\) 都是等概率更靠前的。即 \(\dfrac 12\)

所以只要找到一个串有多少个前后缀即可。假设第 \(i\) 个串有 \(a_i\) 个前缀串,\(b_i\) 个后缀串,答案即为

\[\sum\limits_{i=1}^n b_i + (n-a_i-b_i-1) \times \dfrac 12 \]

显然是 trie 板子,直接做即可。注意 \(\dfrac 12\) 用逆元处理。

代码

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 5e5 + 5, mod = 998244353, inv = 499122177; //inv 是 2 的逆元,也就是 x/2
namespace Trie {
	int tr[N][26], cnt[N]; bool ed[N];
	int j, idx;
	void insert(string s)
	{
		j = 0;
		for (char si : s)
		{
			int i = si - 'a';
			if (!tr[j][i]) tr[j][i] = ++idx;
			j = tr[j][i], cnt[j]++;
		}
		ed[j] = true;
	}
	int query(string s)
	{
		j = 0; int ans = 0;
		for (char si : s) ans += ed[j], j = tr[j][si - 'a'];
		return ans;
	}
}; using namespace Trie;
string a[N];
int main()
{
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i], insert(a[i]);
	for (int i = 1; i <= n; i++)
	{
		int pre = query(a[i]), suf = cnt[j];
		cout << (1 + pre + (1ll * (n - pre - suf) * inv % mod)) % mod << '\n';
	}
	return 0;
}

希望能帮助到大家!

posted @ 2023-05-26 19:56  liangbowen  阅读(23)  评论(0编辑  收藏  举报