Codeforces 775 E Tyler and Strings
Codeforces 775 E Tyler and Strings
題意
給出長度為 \(n\) 的字串 \(s\) 和長度為 \(m\) 的字串 \(t\)。 若將 \(s\) 中字元重新排列,生成字串 \(p\),求字串 \(p\) 的個數,滿足字典序 \(p < t\)。輸出答案對 \(998244353\) 取模後的值。
資料滿足: \(1 \leq n,m,s_i,t_i \leq 2\times 10^5\)。
題解
兩個字串長度關係,共有三種情況:\(|s| < |t|\) , \(|s| = |t|\) 或 \(|s| > |t|\)。
我們先考慮兩個字串長度相等的情況,如果想要 \(p < t\)
- 存在一個位置 \(i(1 \leq i \leq n)\) 滿足 \(p_j = t_j (1 \leq j < i)\) 並且 \(p_i < t_i\)。
對於不同的字串 \(p\) ,我們可以列舉這個第一個小於 \(t\) 串對應字元位置 \(i\) ,那麼 \(p\) 的構造就是:
- \(p_j = t_j (1 \leq j < i)\) , \(p_i\) 位置選擇任意一個小於 \(t_i\) 的字元,\(p_j (i < j \leq n)\) 隨意選擇即可。
我們需要維護字尾地隨意排列構成不同字串地個數,這顯然是一個組合數問題:
\(x_1 + x_2 + x_3 + ... + x_k = tot\),求可以構成不同排列地個數。
答案為: \(\frac{tot!}{x_1! ...x_p! ... x_k!}\)。
如果存在一個某個數字 \(w < t_i\) ,其還剩餘 \(x_p\) 個,那麼如果在 \(s\) 的第 \(i\) 個位置放置該數字,那麼後續可以構成的不重複的字串為:\(\frac{(tot-1)!}{x_1! ...(x_p-1)! ... x_k!}\),此時答案應該加上這個數字。
注意到\(\frac{(tot-1)!}{x_1! ...(x_p-1)! ... x_k!} = \frac{tot!}{x_1! ...x_p! ... x_k!} \times \frac{x_p}{tot}\)
即:\(\frac{tot!}{x_1! ...x_p! ... x_k!} \times \frac{x_{p1} + x_{p2} + ... x_{pr}}{tot}\)
因此需要有一個數據結構,支援單點修改,區間查詢,用樹狀陣列可容易實現。
按照上面的方法,分析\(|s| > |t|\) 和 \(|s| < |t|\) 的兩種情況,發現有一個細節沒有考慮完善:
- 當 \(|s| < |t|\) 時,如果 \(s\) 和 \(t\) 的字首完全相同,那麼會少考慮一次,原因是此時不存在一個位置\(i(1 \leq i \leq n)\) 讓 \(p_i < t_i\),但事實上字典序\(p < t\)成立。
特殊判斷後,本題就能順利通過,時間複雜度 \(O(n \log_2 n)\)。
C++ 程式碼示例
# include <bits/stdc++.h>
# define int long long
# define lowbit(x) (x&(-x))
using namespace std;
const int N = 2e5+10;
const int mo = 998244353;
int fac[N],c[N],a[N],b[N];
void update(int x,int d) {
for (;x<=N-10;x+=lowbit(x)) c[x]+=d;
}
int query(int x) {
int res=0;
for (;x;x-=lowbit(x)) res+=c[x];
return res;
}
int pow(int x,int n) {
int ans = 1;
while (n) {
if (n&1) ans = ans * x %mo;
x=x*x%mo;
n>>=1;
}
return ans;
}
int inv(int x) {
return pow(x,mo-2);
}
signed main() {
int n,m; cin>>n>>m;
map<int,int>tmp;
for (int i=1;i<=n;i++) {
cin>>a[i];
update(a[i],1);
tmp[a[i]]++;
}
for (int i=1;i<=m;i++) cin>>b[i];
fac[0]=1; for (int i=1;i<=N-10;i++) fac[i]=fac[i-1]*i%mo;
int res = fac[n];
for (auto x :tmp) {
res = res * inv(fac[x.second]) % mo;
}
int ans = 0,tot = n;
for (int i=1;i<=min(n,m);i++) {
(ans+=res*inv(tot)%mo*query(b[i]-1)%mo)%=mo;
res=res*inv(tot)%mo*(query(b[i])-query(b[i]-1))%mo;
tot--;
update(b[i],-1);
}
if (n < m) {
sort(a+1,a+1+n);
sort(b+1,b+1+n);
bool f = true;
for (int i=1;i<=n;i++) if (a[i]!=b[i]) {
f = false; break;
}
if (f) ans = (ans+1)%mo;
}
cout<<ans<<endl;
return 0;
}