[洛谷] 纯粹容器(结论/期望)

Problem

题目地址

Solution

可把题目看成:给你一条链,有 \(n-1\) 次删边操作,每次删边把权值较大的点留下来,求每个点期望存活轮数。\(n \le 50\)

结论1:\(x\)\(i\) 左边第一个权值比他大的点,\(y\)\(i\) 右边第一个权值比他大的点,点 \(j\)\(i\) 轮后被删除的的充要条件\(x -> i\) 或者 \(i -> y\) 之间所有的边均被删除,二者有一者满足条件即可。

易证得。

\(f[i,j]\) 表示第 \(i\)\(j\) 仍然存活的概率,则 \(j\) 的期望存活轮数 \(E(j) = \sum f[i,j]\)

\(f[i,j]\) 可以用组合数求得,先求出 \(j\) 在第 \(i\) 轮死去的概率,然后用 \(1\) 减去。即 \(i\) 次删边里面选了 \(x -> i\) 之间所有的边的概率\(i -> y\)同理。注意同时选了 \(x -> i\)\(i -> y\) 的情况会被算两次,要减去一次。具体的:

\[f[i,j] = 1 - (\frac{\dbinom{n-1-d_1}{i-d_1}}{\dbinom{n-1}{i}}+\frac{\dbinom{n-1-d_2}{i-d_2}}{\dbinom{n-1}{i}}-\frac{\dbinom{n-1-d_1-d_2}{i-d_1-d_2}}{\dbinom{n-1}{i}}) \]

其中,\(d_1=i-x,d_2=y-i\),分别 \(i\) 到左右两边比他大的点之间的边数。

预处理 \(O(n^2)\),计算 \(f[i,j]\)\(E(j)\) 的复杂度 \(O(n^2)\),时间复杂度 \(O(n^2)\)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 57, mod = 998244353;
int n;
int a[N],d1[N],d2[N],ans[N];
int f[N][N],c[N][N];
void Init() {
	for(int i=0;i<=n;++i) c[i][0] = 1;
	for(int i=1;i<=n;++i) {
		for(int j=1;j<=i;++j) {
			c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
		}
	}
	a[0] = a[n+1] = INF;
	for(int i=1;i<=n;++i) {
		for(int j=i-1;j>=0;--j) {
			if(a[j] > a[i]) {
				d1[i] = (j>0 ? i - j : INF); break;
			}
		}
		for(int j=i+1;j<=n+1;++j) {
			if(a[j] > a[i]) {
				d2[i] = (j<=n ? j - i : INF); break;
			}
		}
	}
}
int Pow(int x,int y) {
	int res = 1, base = x;
	while(y) {
		if(y&1) res = res*base%mod; base = base*base%mod; y >>= 1;
	}
	return res;
}
int Inv(int x) {
	return Pow(x,mod-2);
}
signed main()
{
	n = read();
	for(int i=1;i<=n;++i) a[i] = read();
	Init();
	for(int i=1;i<n;++i) {
		for(int j=1;j<=n;++j) {
			int x = (d1[j]>i ? 0 : c[n-1-d1[j]][i-d1[j]]*Inv(c[n-1][i])%mod);
			int y = (d2[j]>i ? 0 : c[n-1-d2[j]][i-d2[j]]*Inv(c[n-1][i])%mod);
			int z = (d1[j]+d2[j]>i ? 0 : c[n-1-d1[j]-d2[j]][i-d1[j]-d2[j]]*Inv(c[n-1][i])%mod);
			f[i][j] = (1 - (x+y-z + mod)%mod + mod) % mod;
		}
	}
	for(int i=1;i<n;++i) {
		for(int j=1;j<=n;++j) {
			ans[j] = (ans[j] + f[i][j]) % mod;
		}
	}
	for(int i=1;i<=n;++i) printf("%lld ",ans[i]);
    return 0;
}
/*
5
1 4 2 3 5

499122178 249561091 665496236 582309207 4
*/

Summary

推结论/性质,还是多做题!

posted @ 2020-11-19 15:51  基地AI  阅读(247)  评论(0编辑  收藏  举报