1. 程式人生 > >codeforces EC52 div2E. Side Transmutations(組合數學)

codeforces EC52 div2E. Side Transmutations(組合數學)

題意

給定一個字符集的大小 A |A| ,可以從中選取 n n 個字元(可重複)組成一個字串,這裡規定,字串 S

S 若經過以下一系列變化所得的字串 T T ,那麼認為 S = = T
S == T

  1. 選取有效的 b i b_i ,令 k
    = b i k = b_i
  2. 取字串 S S 的前 k k 個字元組成一個字串 S p r e S_{pre}
  3. 取字串 S S 的後 k k 個字元組成一個字串 S s u f S_{suf}
  4. S p r e , S s u f S_{pre},S_{suf} 各自翻轉並交換組成新的字串T。

輸入規定 b 0 < b i < b i + 1 < < b k b_0<b_i<b_{i+1} < \dots < b_k ,且 b i b_i 可以任選多次。問能組成多少個不同的字串。

題解

對於給定的 0 < b i < b i + 1 < < b k 0 < b_i < b_{i+1} < \dots < b_k ,對於這些操作,現把問題轉換一下,實際上就是對 [ b k 1 , b k ) , [ b k 2 , b k 1 ) [ 0 , b 1 ) [b_{k-1},b_{k}),[b_{k-2},b_{k-1})\dots,[0,b_1) 進行操作,每個區間之間都互不相關,可以看成獨立的n個事件組成一個大事件,即對應的乘法原理,n個事件的方案相乘。設每個區間滿足條件的字串個數為 c n t i cnt_i
c n t i cnt_i 為長度為 i i 的字串的對數。分為兩種情況考慮,將左邊的串稱為L,右邊稱為R,設 S = A i S = |A|^i

  1. 翻轉後 R = L ,那麼只有S種
  2. 翻轉後 R != L,那麼有 S 2 \complement_{S}^{2}

總和為 A 2 i + A i 2 \frac{|A|^{2i}+|A|^i}{2}
對於每個區間都有 c n t l e n cnt_{len} 個字串可選 。那麼總的方案數為
c n t b 1 i = 2 k c n t b i b i 1 A n 2 b k cnt_{b_1}\prod_{i=2}^{k}cnt_{b_i-b_{i-1}} *|A|^{n-2b_k}

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod =  998244353;
const int maxn = 2e5+5;
ll pow_mod(ll x, ll n) {
	ll res = 1;
	while(n) {
		if(n&1) res = res*x%mod;
		x = x*x%mod;
		n >>= 1;
	}
	return res;
}
ll n,m,A,inv;
ll b[maxn];
ll get(ll i) { // cnt_i
	return (pow_mod(A,2*i)+pow_mod(A,i))%mod*inv%mod;
}

int main() {
	inv = pow_mod(2,mod-2);
	scanf("%lld%lld%lld", &n, &m, &A);
	ll mx;
	for(int i = 1; i <= m; ++i) {
		scanf("%lld", &b[i]);
		mx = b[i];
	}
	for(int i = m; i >= 1; --i)
		b[i] = b[i]-b[i-1];
	ll AL = pow_mod(A,n-2*mx);
	ll ans = 1;
	for(int i = 1; i <= m; ++i) {
		ans = (ans*get(b[i]))%mod;
	}
	ans = ans*AL%mod;
	printf("%lld\n", ans);
	return 0;
}