[计数] [组合数学] P10310 「Cfz Round 2」01 String

posted on 2024-06-18 06:19:48 | under | source

拆贡献,考虑 \(f(l,r)\) 会贡献几次,注意到 \(f_k(1,n)\to f(l,r)\) 其实可以反过来视作 \([l,r]\) 区间逐渐扩大 \(k\) 次,最终变成 \([1,n]\) 的过程。

于是我们计算这个东东就好了,左右端点互相独立,以计算左端点 \(l\) 的拓展为例:记 \(p_i\) 表示 \(i\) 会被选为左端点几次,那么等价于计算 \(0\le p_i\)\(\sum\limits_{i=1}^l p_i=k-1\) 的方案数。这里减一是因为必然有一个左端点为 \(1\)

简单组合数学得知其方案数为 \({{k-1+l-1}\choose {l-1}}\),右端点同理。

考虑计算 \(f(l,r)\),每个合法子串只关注其开头和结尾即可,形式为 \(0\dots 10\dots 10\dots 1\) 这样子。注意开头结尾是固定的,而中间为相邻的 \(10\),对它们计数即可。

\(a_i=\sum\limits_{j=1}^{i} [c_i=0\wedge c_{i+1}=1]\),那么 \(f(l,r)=2^{c_{r-2}-c_l}\)

不妨令 \(k\) 先减去 \(1\),那么:

\[ans=\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n} 2^{c_{j-2}-c_i} {{k+i-1}\choose {i-1}} {{k+n-j}\choose n-j} \]

但是 \(k\) 很大,大到 \(\rm lucas\) 定理都解决不了。注意到后面的组合数具有某种规律,可以递推计算。具体来说,令 \(C_i={{k+i}\choose i}\),那么有 \(C_i=\frac{(k+i)C_{i-1}}i\)

(下降幂是啥?蒟蒻不会。)

于是暴力枚举,得到了 \(O(n^2)\) 做法~

进一步优化:

\[ans=\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n} 2^{c_{j-2}-c_i} C_{i-1}C_{n-j} \]

\[ans=\sum\limits_{i=1}^{n-1} 2^{-c_i}C_{i-1}\sum\limits_{j=i+1}^{n} 2^{c_{j-2}} C_{n-j} \]

然后后缀和预处理后面的和式,就能 \(O(n)\) 算出了。

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N = 3e5 + 5, mod = 1e9 + 7;
int T, n, k, inv[N], a[N], ans, C[N];
int s[N];
char c[N];

inline int qstp(int a, int k) {int res = 1; for(; k > 0; a = a * a % mod, k >>= 1) if(k & 1) res = res * a % mod; return res;}
signed main(){
	for(int i = 1; i < N; ++i) inv[i] = qstp(i, mod - 2); 
	cin >> T;
	while(T--){
		ans = 0;
		scanf("%lld%lld %s", &n, &k, c + 1), --k;
		C[0] = 1;
		for(int i = 1; i <= n; ++i) C[i] = C[i - 1] * ((i + k) % mod) % mod * inv[i] % mod;
		for(int i = 1; i < n; ++i) a[i] = a[i - 1] + (c[i] == '1' && c[i + 1] == '0');
		s[n + 1] = 0; 
		for(int i = n; i >= 2; --i){
			s[i] = s[i + 1];
			if(c[i] == '1') s[i] = (s[i] + qstp(2, a[i - 2]) * C[n - i] % mod) % mod;
		}
		for(int i = 1; i < n; ++i){
			if(c[i] == '0'){
				//貌似有点边界问题,所以把j=i+1单独拿出来算
				if(c[i + 1] == '1') ans = (ans + C[i - 1] * C[n - i - 1] % mod) % mod;
				ans = (ans + qstp(inv[2], a[i]) * C[i - 1] % mod * (s[i + 2] + mod) % mod) % mod;
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2026-01-12 20:14  Zwi  阅读(2)  评论(0)    收藏  举报