[ABC268G] Random Student ID Solution
目录更好的阅读体验戳此进入
题面
给定 $ n $ 名学生的名字 $ S_i $,保证不重复。现在随机一个 $ a \rightarrow z $ 的排列作为字典序的偏序关系,求每个同学排名的期望,对 $ 998244353 $ 取模。
Solution
这一题可以说确实很高妙,确实没想到还可以这么处理。
第一眼以为是一些类似 期望DP 的东西,然后想了半天也没想到什么复杂度正确的做法,实际上这道题是从期望的意义去找性质。
首先我们考虑对于一个串 $ S $ 的期望排名,若存在串 $ S $ 的前缀串 $ T $,那么显然串 $ T $ 无论字典序的偏序关系如何都会在 $ S $ 之前,所以一定会贡献 $ 1 $。
而若存在 $ S $ 是 $ T $ 的前缀,那么显然 $ T $ 在 $ S $ 之后,则贡献一定为 $ 0 $。
对于其它的情况,显然会由 $ \texttt{LCP} $ 之后的第一个不同的字符决定,而对于这种显然在所有字典序关系中两者的偏序关系的概率是相同的,所以此时的贡献为 $ \dfrac{1}{2} $。那么对于字符串 $ S $,若其前缀串有 $ x $ 个,满足有 $ S $ 前缀的串有 $ y $ 个,那么答案即为:
\[x \times 1 + y \times 0 + (n - x - y - 1) \times \dfrac{1}{2} + 1 \]然后对于求前缀,显然可以通过 Trie树 解决,同时也可以考虑一些更简单的方法:
开一个 unordered_set
,往里面丢每个串的哈希值,然后对于每个串的每个前缀的哈希在里面查一下然后记录即可,最终复杂度 $ O(\sum \lvert S_i \rvert) $。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
#define MOD (998244353ll)
#define BASE (31ll)
template < typename T = int >
inline T read(void);
int N;
int cntx[510000], cnty[510000];
ll inv_2;
string S[510000];
unordered_map < unll, int > idx;
ll qpow(ll a, ll b){
ll ret(1), mul(a);
while(b){
if(b & 1)ret = ret * mul % MOD;
b >>= 1;
mul = mul * mul % MOD;
}return ret;
}
int main(){
inv_2 = qpow(2, MOD - 2);
N = read();
for(int i = 1; i <= N; ++i){
cin >> S[i];
unll cur(0);
for(auto c : S[i])(cur *= BASE) += c;
idx.insert({cur, i});
}
for(int i = 1; i <= N; ++i){
unll cur(0);
for(auto c : S[i]){
(cur *= BASE) += c;
if(idx.find(cur) != idx.end())++cntx[i], ++cnty[idx[cur]];
}cntx[i]--, cnty[i]--;
}
for(int i = 1; i <= N; ++i)
printf("%lld\n", (cntx[i] + (N - cntx[i] - cnty[i] - 1) * inv_2 % MOD + 1) % MOD);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
UPD
update-2023_01_18 初稿
标签:return,前缀,ABC268G,题解,ll,Random,ret,int,define From: https://www.cnblogs.com/tsawke/p/17124349.html