CF1151F Sonya and Informatics

给一个长度为 n(n100)n (n\leq 100)0/10/1 串,进行 k(k109)k (k \leq 10^9) 次操作,每次操作选择两个位置 i,j(1i<jn)i,j (1 \leq i < j \leq n),交换 i,ji,j 上的数,求 kk 次操作后,该 0/10/1 串变成非降序列的概率,答案对 109+710^9+7 取模。

sol

好题,概率 dp

设有 mm00,那么题意就是让 a[1,m]a[1,m] 均为 00a[m+1,n]a[m+1,n] 均为 11

fi,jf_{i,j} 表示 ii 个操作后,前 mm 个数中有 jj00 的方案数,答案即为 fk,mi=0mfk,i\frac{f_{k,m}}{\sum\limits_{i=0}^{m}f_{k,i}},边界:f0,p=1f_{0,p}=1pp 为原序列前 mm 个数中 00 的个数。

对于 fi,jf_{i,j},考虑它是如何转移来的:

  • 之前有 j1j-100,第 ii 次交换换来一个 00,由于前面 11 的个数与后面 00 的个数均为 mj+1m-j+1,顾方案数为 fi1,j1×(mj+1)2f_{i-1,j-1}\times (m-j+1)^2
  • 之前有 j+1j + 100,第 ii 次交换换走一个 00,由于前面有 j+1j+100,后面有 nm(mj1)=n2m+j+1n-m-(m-j-1)=n-2m+j+111,顾方案数为 fi1,j+1×(j+1)(n2m+j+1)f_{i-1,j+1}\times (j+1)(n-2m+j+1)
  • 之前本来就有 jj00,第 ii 次操作没换走也没换来,四种情况:前面交换,后面交换,前后交换 00,前后交换 11,则方案数为 Cm2+Cnm2+j(mj)+(mj)(n2m+j)C_{m}^{2}+C_{n-m}^{2}+j(m-j)+(m-j)(n-2m+j)

到这里差点结束了,总结:fi,j=fi1,j1×(mj+1)2+fi1,j+1×(j+1)(n2m+j+1)+Cm2+Cnm2+j(mj)+(mj)(n2m+j)f_{i,j}=f_{i-1,j-1}\times (m-j+1)^2+f_{i-1,j+1}\times (j+1)(n-2m+j+1)+C_{m}^{2}+C_{n-m}^{2}+j(m-j)+(m-j)(n-2m+j)

考虑到 k109k\leq 10^9,经验告诉我们直接上矩阵快速幂,毕竟这转移无需判断。

[c0b1000a0c1b2000a1c2b3000a2c30000a300000cm]×[fi1,0fi1,1fi1,2fi1,3fi1,4fi1,m]=[fi,0fi,1fi,2fi,3fi,4fi,m]\begin{bmatrix}c_0& b_1& 0 & 0&\cdots & 0\\ a_0 & c_1 & b_2 & 0&\cdots & 0\\ 0 & a_1 & c_2 & b_3 & \cdots & 0\\ 0&0&a_2&c_3&\cdots&0\\ 0&0&0&a_3&\cdots&0\\ \cdots&\cdots&\cdots&\cdots&\cdots&\cdots\\ 0&0&0&0&\cdots&c_m\end{bmatrix} \times \begin{bmatrix}f_{i-1,0}\\f_{i-1,1}\\f_{i-1,2}\\f_{i-1,3}\\f_{i-1,4}\\ \cdots \\f_{i-1,m}\end{bmatrix}=\begin{bmatrix}f_{i,0}\\f_{i,1}\\f_{i,2}\\f_{i,3}\\f_{i,4}\\ \cdots \\f_{i,m}\end{bmatrix}

其中 ai=(mi)2a_i=(m-i)^2bi=i(n2m+i)b_i=i(n-2m+i)ci=Cm2+Cnm2+i(mi)+(mi)(n2m+i)c_i=C_{m}^{2}+C_{n-m}^{2}+i(m-i)+(m-i)(n-2m+i)

时间复杂度 O(n3logk)\mathcal O(n^3\log k)

#include <bits/stdc++.h>

using namespace std;

#define int long long

inline int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		x = (x << 3) + (x << 1) + (c ^ 48);
		c = getchar();
	}
	return x * f;
}

const int _ = 107, p = 1e9 + 7;

int n, k, m, t, Ans, A[_], a[_], b[_], c[_];

struct matrix {
	int a[_][_];
	void init() {
		memset(a, 0, sizeof a);
	}
	matrix mul(matrix x) {
		matrix res;
		res.init();
		for (int i = 0; i <= m; ++i)
			for (int j = 0; j <= m; ++j)
				for (int k = 0; k <= m; ++k)
					res.a[i][j] = (res.a[i][j] + a[i][k] * x.a[k][j] % p) % p;
		return res;
	}
} ans, base;

void Qpow(int k) {
	while (k) {
		if (k & 1)
			ans = ans.mul(base);
		k >>= 1, base = base.mul(base);
	}
}

inline int qpow(int x, int y) {
	int res = 1;
	while (y) {
		if (y & 1)
			res = res * x % p;
		y >>= 1, x = x * x % p;
	}
	return res;
}

signed main() {
	n = read(), k = read();
	for (int i = 1; i <= n; ++i) A[i] = read();
	for (int i = 1; i <= n; ++i) m += (A[i] == 0);
	for (int i = 1; i <= m; ++i) t += (A[i] == 0);
	for (int i = 0; i <= m; ++i) {
		a[i] = (m - i) * (m - i) % p;
		b[i] = i * (n - 2 * m + i) % p;
		c[i] = (m * (m - 1) * qpow(2, p - 2) % p + (n - m) * (n - m - 1) * qpow(2, p - 2) % p + (m - i) * (n - 2 * m + 2 * i) % p) % p;
	}
	ans.init();
	for (int i = 0; i <= m; ++i) ans.a[i][i] = 1;
	base.init();
	base.a[0][0] = c[0], base.a[0][1] = b[1];
	base.a[m][m - 1] = a[m - 1], base.a[m][m] = c[m];
	for (int i = 1; i <= m - 1; ++i) {
		base.a[i][i - 1] = a[i - 1];
		base.a[i][i] = c[i];
		base.a[i][i + 1] = b[i + 1];
	}
	Qpow(k);
	base.init();
	base.a[t][0] = 1;
	ans = ans.mul(base);
	for (int i = 0; i <= m; ++i)
		Ans = (Ans + ans.a[i][0]) % p;
	printf("%lld", ans.a[m][0] * qpow(Ans, p - 2) % p);
	return 0;
}
posted @   蒟蒻orz  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示