首页 > 其他分享 >【luogu CF618G】Combining Slimes(矩阵乘法)(DP)

【luogu CF618G】Combining Slimes(矩阵乘法)(DP)

时间:2022-09-29 17:47:30浏览次数:94  
标签:frac matrix limits min int luogu sum Slimes Combining

Combining Slimes

题目链接:luogu CF618G

题目大意

有一个长度为 n 的栈,如果栈顶两个值都是 x 就会合并成 x+1,一开始没有东西。
你有 p 的概率放进去一个 1,1-p 的概率放入 2,问你当栈被放满的时候,你的期望分数。
分数是栈里所有值的和。

思路

考虑统计每个位置的数的贡献。


由于某个位置的数会因为合并改变,考虑先搞点简单的,某个位置出现过

\(a_{i,j}\) 为用了 \(i\) 个位置,最左边是 \(j\) 的概率:
初始:\(a_{i,1}=p,a_{i,2}=1-p\)
转移:\(a_{i,j}=a_{i,j-1}a_{i-1,j-1}\)(就是后面的两个位置都是 \(j-1\),合并成 \(j\))
发现 \(i>1\) 的时候 \(a_{i,1},a_{i,2}\) 又要初始又要转移,那就特判一下都加上。


那我们要的不是出现过,而是它不会被合并掉,最后留了下来。
\(A_{i,j}\) 为用了 \(i\) 个位置,最左边是 \(j\) 而且不会被合并掉的概率:
初始化:\(A_{1,1}=p,A_{1,2}=1-p\)
\(A_{i,j}=a_{i,j}(1-a_{i-1,j})\)


那我们就可以试着算算期望了:
\(f_{i,j}\) 为最后序列右边 \(i\) 位最左边是 \(j\) 的情况下,这 \(i\) 位的期望和。
那就直接枚举右边 \(i-1\) 位的,考虑一下范围。

不难想想,只有两种可能,要么是你自己是 \(1\),它不是 \(1\),要么它就比你小。
\(1\) 那个显然,如果你不是 \(1\),它比你打一定要从至少 \(2\) 往上一步一步加,那 \(2\) 到它那个数之间的数都要经历过。
那到你那个数的时候就合并了啊!


然后发现 \(2\) 这个好像很特别,似乎要再 DP 一下:
\(b_{i,j}\) 为长度为 \(i\) 的,第一次放进来的是 \(2\),最左边是 \(j\) 的概率:
初始化:\(b_{i,2}=1-p\)
\(b_{i,j}=b_{i,j-1}a_{i-1,j-1}\)


同样的道理,我们也要求不变的:
\(B_{i,j}\) 为长度为 \(i\),第一次放进来是 \(2\),最左边是 \(j\) 而且不会被合并的概率:
初始化:\(B_{1,2}=1-p\)
\(B_{i,j}=b_{i,j}(1-a_{i-1,j})\)


这些都弄好之后,我们正式开始搞 \(f\) 的转移:
\(f_{i,j}=j+\frac{\sum\limits_{k=2}^{n}f_{i-1,k}B_{i-1,k}}{\sum\limits_{k=2}^mB_{i-1,k}}(j=1)\)
\(f_{i,j}=j+\frac{\sum\limits_{k=1}^{j-1}f_{i-1,k}A_{i-1,k}}{\sum\limits_{k=1}^{j-1}A_{i-1,k}}(j\neq 1)\)

那我们就可以转移啦!

但是 \(n\) 很大,那你这个是 \(n^2+nm\) 的啊。

发现一个事情,你会觉得它一直没有 \(1\) 接着 \(2\) 的情况其实会很少。
而且你要注意到你凑出 \(x\) 你至少需要 \(2^{x-2}\) 次。
那比如一个 \(50\),那不出现 \(1,2\) 的概率就是 \((1-p(1-p))^{2^{50}}\),只要下面不是 \(1\) 基本上就宣告约等于 \(0\) 了。
那合并的概率也就是 \(0\)。

也就是说,我们设 \(m=50\),那 \(j\geqslant m\) 的时候,我们可以认为 \(A_{i,j}=0,B_{i,j}=0\)。
然后发现 \(i,j\) 太大的 \(i\) 也不好搞啊,但是你看看那个式子:
\(A_{i,j}=a_{i,j}(1-a_{i-1,j})\),\(B_{i,j}\) 也差不多。
那 \(j\) 很大的时候,\(a_{i-1,j},a_{i,j}\) 都很小,那 \(A_{i,j}\) 不也很小,那就 \(0\) 咯,直接忽略,所以也只用看到 \(m\)。
(毕竟这题是有精度要求的,不是取模那些)


那再看式子:

\(f_{i,j}=j+\frac{\sum\limits_{k=2}^{\min(n,m)}f_{i-1,k}B_{\min(i-1,m),k}}{\sum\limits_{k=2}^{\min(n,m)}B_{\min(i-1,m),k}}(j=1)\)
\(f_{i,j}=j+\frac{\sum\limits_{k=1}^{\min(j-1,m)}f_{i-1,k}A_{\min(i-1,m),k}}{\sum\limits_{k=1}^{\min(j-1,m)}A_{\min(i-1,m),k}}(j\neq 1)\)

然后我们如果把 \(\leqslant m\) 的单独暴力处理,然后看 \(>m\) 的部分:
\(f_{i,j}=j+\frac{\sum\limits_{k=2}^{m}f_{i-1,k}B_{m,k}}{\sum\limits_{k=2}^{m}B_{m,k}}(j=1)\)
\(f_{i,j}=j+\frac{\sum\limits_{k=1}^{m}f_{i-1,k}A_{m,k}}{\sum\limits_{k=1}^{m}A_{m,k}}(j\neq 1)\)

怎么感觉,有点像那种递推的式子。
看看,会发现确实可以用 \(1\times m\) 的矩阵表示 \(f_i\)。
然后转移矩阵因为 \(i-1\) 都给你变成了 \(j\),所以是固定的。

那直接上矩阵快速幂就可以啦!

代码

#include<cstdio>

using namespace std;

const int N = 55;
int n, m;
double p, a[N][N], A[N][N], b[N][N], B[N][N], f[N][N];
struct matrix {
	int n, m;
	double a[N][N];
}A_, B_;

matrix operator *(matrix x, matrix y) {
	matrix z; z.n = x.n; z.m = y.m;
	for (int i = 0; i < z.n; i++)
		for (int j = 0; j < z.m; j++)
			z.a[i][j] = 0;
	for (int k = 0; k < x.m; k++)
		for (int i = 0; i < z.n; i++)
			for (int j = 0; j < z.m; j++)
				z.a[i][j] += x.a[i][k] * y.a[k][j];
	return z;
}

matrix ksm(matrix x, int y) {
	matrix z = x; y--;
	while (y) {
		if (y & 1) z = z * x;
		x = x * x; y >>= 1;
	}
	return z;
}

int main() {
	scanf("%d %lf", &n, &p);
	p /= 1000000000; m = 50;
	
	a[1][1] = p; a[1][2] = 1 - p;
	for (int i = 2; i <= m; i++) {
		a[i][1] = p; a[i][2] = 1 - p + a[i][1] * a[i - 1][1];
		for (int j = 3; j <= m; j++)
			a[i][j] = a[i][j - 1] * a[i - 1][j - 1];
	}
	A[1][1] = p; A[1][2] = 1 - p;
	for (int i = 2; i <= m; i++) {
		for (int j = 1; j <= m; j++)
			A[i][j] = a[i][j] * (1 - a[i - 1][j]);
	}
	b[1][2] = 1 - p;
	for (int i = 2; i <= m; i++) {
		b[i][2] = 1 - p;
		for (int j = 3; j <= m; j++)
			b[i][j] = b[i][j - 1] * a[i - 1][j - 1];
	}
	B[1][2] = 1 - p;
	for (int i = 2; i <= m; i++) {
		for (int j = 1; j <= m; j++)
			B[i][j] = b[i][j] * (1 - a[i - 1][j]);
	}
	f[1][1] = 1; f[1][2] = 2;
	for (int i = 2; i <= m; i++) {
		double sum = 0;
		for (int k = 2; k <= m; k++) {
			f[i][1] += f[i - 1][k] * B[i - 1][k]; sum += B[i - 1][k];
		}
		f[i][1] = f[i][1] / sum + 1;
		for (int j = 2; j <= m; j++) {
			sum = 0;
			for (int k = 1; k < j; k++) {
				f[i][j] += f[i - 1][k] * A[i - 1][k];
				sum += A[i - 1][k];
			}
			f[i][j] = f[i][j] / sum + j;
		}
	}
	
	if (n <= m) {
		double ans = 0;
		for (int i = 1; i <= m; i++)
			ans += A[n][i] * f[n][i];
		printf("%.10lf", ans); return 0;
	}
	
	A_.n = 1; A_.m = m + 1; A_.a[0][0] = 1;
	for (int i = 1; i <= m; i++) A_.a[0][i] = f[m][i];
	B_.n = m + 1; B_.m = m + 1; B_.a[0][0] = 1;
	for (int i = 1; i <= m; i++) {
		B_.a[0][i] = i; if (i == 1) continue; double sum = 0;
		for (int j = 1; j < i; j++) sum += A[m][j];
		for (int j = 1; j < i; j++) B_.a[j][i] = A[m][j] / sum;
	}
	double sum = 0;
	for (int i = 2; i <= m; i++) sum += B[m][i];
	for (int i = 2; i <= m; i++) B_.a[i][1] = B[m][i] / sum;
	
	B_ = ksm(B_, n - m); A_ = A_ * B_;
	double ans = 0;
	for (int i = 1; i <= m; i++)
		ans += A[m][i] * A_.a[0][i];
	printf("%.10lf", ans); return 0;
	
	return 0;
}

标签:frac,matrix,limits,min,int,luogu,sum,Slimes,Combining
From: https://www.cnblogs.com/Sakura-TJH/p/luogu_CF618G.html

相关文章

  • luogu P5518 幽灵乐团 / 莫比乌斯反演基础练习题
    重新学习了一下莫比乌斯反演,再来写一次这道题。Part1\[\prod_{i=1}^A\prod_{j=1}^B\prod_{k=1}^C\dfrac{\text{lcm}(i,j)}{\gcd(i,k)}\]\[=\prod_{i=1}^A\prod_{j=1}^B......
  • [luogu p2160] [SHOI2007]书柜的尺寸
    [P2160SHOI2007]书柜的尺寸-洛谷|计算机科学教育新生态(luogu.com.cn)把书按高度从大到小排序,依次考虑放置,每一层第一个被放置的书的高度就是这一层的高度。设\(f......
  • Luogu2607 & Luogu1453 基环树dp
    2607:一个基环树,有点权,全是有向边,选儿子则不能选父亲(反之亦然),问选出的集合的最大点权和注意到题目的特殊性,如果i->hatred[i],那么就是一个内向树,否则为外向树内向树好找环,......
  • luogu P4677 山区建小学
    山区建小学题目描述政府在某山区修建了一条道路,恰好穿越总共\(n\)个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距......
  • luogu P1043 [NOIP2003 普及组] 数字游戏
    [NOIP2003普及组]数字游戏题目描述丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容......
  • luogu P1385 密令
    密令题目描述给定一小写字母串\(s\),每次操作你可以选择一个\(p\)(\(1\leqp\lt|s|\))执行下述修改中的任意一个:将\(s_p\)改为其字典序\(+1\)的字母,将\(s_{p+1}......
  • 【luogu P6419】Kamp(换根DP)
    Kamp题目链接:luoguP6419题目大意一棵树上有一些点有人,边有通过的长度,然后对于每个点,你从这个点出发经过所有人(不用回到原来位置)的最短时间。其它人不会动,只有你去找人......
  • [luogu3980]志愿者招募
    记$x_{i}$为第$i$类志愿者数量$,y_{j}=\sum_{j\in[s_{i},t_{i}]}x_{i}-a_{j}$​,则问题即$$\foralli\in[1,m],x_{i}\ge0\\\forallj\in[1,n],y_{j}\ge0\\y_{1}-\sum_{......
  • 【luogu CF1109E】Sasha and a Very Easy Test(线段树)
    SashaandaVeryEasyTest题目链接:luoguCF1109E题目大意维护一个长度为n的序列,有区间乘,单点除(保证能整除),区间求和答案对p取模。p不一定是质数。思路麻了考场......
  • [luogu p8251] [NOI Online 2022 提高组] 丹钓战
    [P8251NOIOnline2022提高组]丹钓战-洛谷|计算机科学教育新生态(luogu.com.cn)容易发现对于一次查询\([L,R]\),\(L\)一定是第一个入栈的,也是成功的,答案至少为......