zhengrui26NOIP十连测day5-T3

https://www.zhengruioi.com/problem/3695?cid=2210
image
时间复杂度要求:\(\mathcal O(n|\Sigma|)\),其中\(|\Sigma|\) 是字符集大小。


首先,我们考虑转化。
我们假设现在要求字符 \(T\) 的答案。
我们记 \(t_{a,b}\) 表示有多少个字符串 \(S\) 满足 \(S<T\) 的充要条件是 \(a<b\),其中 \(a,b\) 是两个字符。
并记

\[I_{a,b}=\begin{cases} 1,a排名比b靠前\\ 0,b排名比a靠前 \end{cases} \]

则一个单词的排名

\[X_i=1+\sum_{a\not=b}t_{a,b}I_{a,b} \]

这样有点不太妙,我们把它改成

\[X_i=C+\sum_{a<b}t_{a,b}I_{a,b} \]

其中 \(C\) 是一个不重要常数,而此处 \(t\) 的含义与原来不太一样,具体看后面。
这个为什么可以呢?
我们考虑 \(I_{a,b}=1-I_{b,a}\)。
原来那个式子等价于

\[\begin{align*} X_i&=1+\sum_{a<b}(t_{a,b}I_{a,b}+t_{b,a}I_{b,a})\\ &=1+\sum_{a<b}(t_{a,b}I_{a,b}+t_{b,a}-t_{b,a}I_{a,b})\\ &=C+\sum_{a<b}(t_{a,b}-t_{b,a})I_{a,b} \end{align*} \]

所以我们新的 \(t_{a,b}\) 的定义就是原来的 \(t_{a,b}\) 减去一个 \(t_{b,a}\),我们在统计 \(t_{a,b}\) 的时候若 \(a>b\),就给 \(t_{b,a}\) 减去要加的的数就行。
这个常数 \(C\) 为什么不重要呢?
我们考虑

\[\begin{align*} &E((X + C)^2)-E((X+C))^2\\ =&E(X^2+2XC+C^2)-(E(X)+C)^2\\ =&E(X^2)+2CE(X)+C^2-E(X)^2-2CE(X)-C^2\\ =&E(X^2)-E(X)^2 \end{align*} \]

所以一个常数对答案没有影响。
所以我们要求的就是 \(E(S^2)-E(S)^2\),其中

\[S=\sum_{a<b}t_{a,b}I_{a,b} \]


为什么题面管那个东西叫方差?
众所周知,方差的定义是 \(Var(X)=E((X-E(X))^2)\)。
我们进行推导。
我们先看括号里的东西。

\[(X-E(X))^2=X^2-2XE(X)+E(X)^2 \]

我们对这个东西取期望。
就是

\[E(X^2)-2E(XE(X))+E(E(X)^2) \]

注意到,\(E(x)\) 是一个常数。
所以这个东西也就是

\[E(X^2)-2E(X)E(X)+E(X)^2=E(X^2)-E(X)^2 \]

得证。


得到了这个结论,我们就可以使用一个好优化的 \(Var(X)\) 的形式。
我们令 \(S=\sum_{i}X_i\),这里的 \(i\) 对应到之前提到的 \(S\) 就是一个满足 \(a<b\) 的 \((a,b)\) 点对,而 \(X_i\) 也就是 \(t_{a,b}I_{a,b}\)。
ps:下文提到的任意一个点对 \((x,y)\) 都附带 \(x<y\) 的条件。
我们这里先用 \(X_i\) 进行代替。
我们要算 \(Var(S)\),还是先看括号里的东西

\[\begin{align*} (S-E(S))^2&=(\sum_i X_i-E(\sum_i X_i))^2\\ &=(\sum_i X_i-\sum_i E(X_i))^2\\ &=(\sum_i (X_i-E(X_i)))^2\\ &=\sum_i (X_i-E(X_i))^2+2\sum_{i<j}(X_i-E(X_i))(X_j-E(X_j)) \end{align*} \]

我们对这个东西取期望,就是

\[\sum_i Var(X_i)+2\sum_{i<j}E((X_i-E(X_i))(X_j-E(X_j))) \]

我们把后面这一大坨 \(E((X_i-E(X_i))(X_j-E(X_j)))\) 叫做 \(Cross(X_i,X_j)\),中文名是协方差。
有一个结论,\(Cross(A,B)=E(AB)-E(A)E(B)\)。
为什么呢?
我们还是先考虑括号里面的东西,即 \((X_i-E(X_i))(X_j-E(X_j))\)。
这个东西就是

\[X_iX_j-E(X_i)X_j-E(X_j)X_i+E(X_i)E(X_j) \]

我们给它取期望,就是

\[E(X_iX_j)-E(E(X_i)X_j)-E(E(X_j)X_i)+E(X_i)E(X_j) \]

我们再次注意到 \(E(X_i)\) 和 \(E(X_j)\) 都是常数。
因此这个东西就是

\[E(X_iX_j)-E(X_i)E(X_j)-E(X_j)E(X_i)+E(X_i)E(X_j) \]

即

\[E(X_iX_j)-E(X_i)E(X_j) \]

所以

\[Var(S)=\sum_i Var(X_i)+2\sum_{i<j}(E(X_iX_j)-E(X_i)E(X_j)) \]

我们把 \(i=(a,b)\),\(j=(c,d)\) 回带。
此时发现枚举 \(i<j\) 相当于是枚举两个不同的点对 \((a,b),(c,d)\),且要求不重复枚举 \((a,b),(c,d)\) 和 \((c,d),(a,b)\)。
则

\[Var(S)=\sum_{a<b}Var(t_{a,b}I_{a,b})+2\sum_{P(a,b,c,d)}(E(t_{a,b}I_{a,b}t_{c,d}I_{c,d})-E(t_{a,b}I_{a,b})E(t_{c,d}I_{c,d})) \]

其中 \(P(a,b,c,d)\) 表示上面所说的:枚举两个不同的点对 \((a,b),(c,d)\),且要求不重复枚举枚举 \((a,b),(c,d)\) 和 \((c,d),(a,b)\)。
因为 \(t_{a,b},t_{c,d}\) 是常数,所以我们可以提出来。
注意到 \(Var\) 是二次的,所以 \(t_{a,b}\) 提出来是 \(t_{a,b}^2\)。
右面那个东西的系数也可以提出来,得到:

\[Var(S)=\sum_{a<b}t_{a,b}^2Var(I_{a,b})+2\sum_{P(a,b,c,d)}t_{a,b}t_{c,d}(E(I_{a,b}I_{c,d})-E(I_{a,b})E(I_{c,d})) \]

对于左边那一项,\(Var(I_{a,b})=E(I_{a,b}^2)-E{I_{a,b}}^2\),注意到 \(I\) 要么是 \(0\),要么是 \(1\),因此 \(I_{a,b}^2=I_{a,b}\)。
又因为在随机排列中,\(a<b\) 的概率是 \(\frac{1}{2}\),因此 \(Var(I_{a,b})=\frac{1}{2}-\frac{1}{4}=\frac{1}{4}\)、
对于 \(Var(S)\) 的右面那一大坨东西,我们发现当 \(a,b,c,d\) 互不相同的时候,\(I_{a,b}\) 和 I_{c,d}$ 彼此独立,因此此时 \(E(I_{a,b}I_{c,d})=E(I_{a,b})E(I_{c,d})\),\(Cross\) 的贡献为 \(0\)。
因此我们右边相当于枚举三个字母 \(a,b,c\),而此时点对的形状就是这三种:

\[\begin{align*} (a,b),(a,c)\\ (a,b),(b,c)\\ (a,c),(b,c) \end{align*} \]

考虑第一个形状。
列举全排列:

\[\begin{align*} a,b,c\\ a,c,b\\ b,a,c\\ b,c,a\\ c,a,b\\ c,b,a \end{align*} \]

发现 \(a<b\) 且 \(a<c\) 的概率是 \(\frac{1}{3}\)。
因此 \(E(I_{a,b}I_{a,c})-E(I_{a,b})E(I_{a,c})=\frac{1}{3}-\frac{1}{4}=\frac{1}{12}\)。
而第二个形状的贡献则是 \(\frac{1}{6}-\frac{1}{4}=-\frac{1}{12}\)。
第三个形状的贡献是 \(\frac{1}{3}-\frac{1}{4}=\frac{1}{12}\)。
此时那坨东西还是不好处理。
怎么办?
上文中,我们只对满足 \(a<b\) 的 \(t_{a,b}\) 处理了贡献,而这里我们定义 \(t_{b,a}=-t_{a,b},a<b\)。
并直接给出右面那个东西的等价形式:

\[2\times\frac{1}{12}\sum_{a}\sum_{b<c,a\not=b,a\not=c}t_{a,b}t_{a,c} \]

不证明了,好好想想。
则

\[Var(S)=\frac{1}{4}\sum_{a<b}t_{a,b}^2+\frac{1}{6}\sum_{a}\sum_{b<c,a\not=b,a\not=c}t_{a,b}t_{a,c}=\frac{3\sum_{a<b}t_{a,b}^2+2\sum_{a}\sum_{b<c,a\not=b,a\not=c}t_{a,b}t_{a,c}}{12} \]

我们发现这个很不好看。
我们便定义

\[\delta_a=\sum_{b\not=a}t_{a,b} \]

我们再次直接给出等价形式

\[\frac{\sum_{a<b}t_{a,b}^2+\sum_a \delta_a^2}{12} \]

如何处理 \(t\) 和 \(\delta\)?
直接在trie上跳就行。
代码:

点击查看代码
#include <iostream>
#include <string>

using std::cin;
using std::cout;
const int N = 2e5 + 10;
const int mod = 998244353;

int pow(int x, int k)
{
	int ret = 1;
	for (; k; x = 1ll * x * x % mod, k >>= 1)
	{
		if (k & 1)
			ret = 1ll * ret * x % mod;
	}
	return ret;
}

int tot;
int mul;
int sum;
std::string s;
int id[N];
int d[26];
int siz[N];
int ans[N];
int t[26][26];
int son[N][26];

void ins(std::string s, int i)
{
	int now = 0;
	for (auto c : s)
	{
		int x = c - 'a';
		if (!son[now][x])
			son[now][x] = ++tot;
		now = son[now][x];
	}
	id[now] = i;
}
void chd(int x, int k)
{
	sum -= 1ll * d[x] * d[x] % mod;
	if (sum < 0)
		sum += mod;
	d[x] += k;
	if (d[x] >= mod)
		d[x] -= mod;
	if (d[x] < 0)
		d[x] += mod;
	sum += 1ll * d[x] * d[x] % mod;
	if (sum >= mod)
		sum -= mod;
}
void cht(int x, int y, int k)
{
	chd(x, k);
	if (x < y)
	{
		sum -= 1ll * t[x][y] * t[x][y] % mod;
		if (sum < 0)
			sum += mod;
	}
	t[x][y] += k;
	if (t[x][y] >= mod)
		t[x][y] -= mod;
	if (t[x][y] < 0)
		t[x][y] += mod;
	if (x < y)
	{
		sum += 1ll * t[x][y] * t[x][y] % mod;
		if (sum >= mod)
			sum -= mod;
	}
}
void dfs(int x)
{
	siz[x] = !!(id[x]);
	for (int i = 0; i < 26; ++i)
	{
		int s = son[x][i];
		if (!s)
			continue;
		dfs(s);
		siz[x] += siz[s];
	}
}
void get(int x)
{
	if (id[x])
		ans[id[x]] = 1ll * sum * mul % mod;
	for (int i = 0; i < 26; ++i)
	{
		int s = son[x][i];
		if (!s)
			continue;
		for (int j = 0; j < i; ++j)
		{
			int ss = son[x][j];
			if (ss)
			{
				cht(j, i, siz[ss]);
				cht(i, j, -siz[ss]);
			}
		}
		for (int j = i + 1; j < 26; ++j)
		{
			int ss = son[x][j];
			if (ss)
			{
				cht(i, j, -siz[ss]);
				cht(j, i, siz[ss]);
			}
		}
		get(s);
		for (int j = 0; j < i; ++j)
		{
			int ss = son[x][j];
			if (ss)
			{
				cht(j, i, -siz[ss]);
				cht(i, j, siz[ss]);
			}
		}
		for (int j = i + 1; j < 26; ++j)
		{
			int ss = son[x][j];
			if (ss)
			{
				cht(i, j, siz[ss]);
				cht(j, i, -siz[ss]);
			}
		}
	}
}

int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		cin >> s;
		ins(s, i);
	}
	mul = pow(12, mod - 2);
	dfs(0);
	get(0);
	for (int i = 1; i <= n; ++i)
		cout << ans[i] << '\n';
	return 0;
}

时间复杂度 \(\mathcal O(|\Sigma|n)\)

posted @ 2026-10-22 20:05  SigmaToT  阅读(7)  评论(0)    收藏  举报