codeforces EC52 div2E. Side Transmutations(组合数学)

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/83317239

题意

给定一个字符集的大小 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;
}

猜你喜欢

转载自blog.csdn.net/Link_Ray/article/details/83317239