1. 程式人生 > 其它 >Codeforces 775 E Tyler and Strings

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\)

,字串 \(p\) 必然滿足:

  • 存在一個位置 \(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}\)

,那麼如果存在很多不同的\(w < t_i\),我們可以合併計算。

即:\(\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;
}