首先先看几个通用的知识点:
1.费马小定理+快速幂求逆元(求倒数)
当mod为质数的时候可以使用费马小定理
ll ksm(int x, int y) {
if (x == 1) return 1;
ll res = 1, base = x;
while (y) {
if (y & 1) res = (res * base) % mod;
base = (base * base) % mod;
y >>= 1;
}
return res;
}
int inv(int aim)//inverse element
{
return ksm(aim, mod - 2);
}
2.动态规划预处理组合数
i个里面选j个
公式:C[i][j] = C[i-1][j] +C[i-1][j-1] 。
组合公式理解, 从 i 里选 j 个的方案数由两部分,设k是 i 中某一个, 则选取 k 和不选取 k :C[i-1][j] + C[i-1][j-1]
(第i个选或不选)
初始化 C[ i ][ 0 ] = 1; (起源都算一种)
int C[105][105] = { 0 };
C[0][0] = 1;
for (int i = 1; i <= n; i++)
{
C[i][0] = 1;
for (int j = 1; j <= i; j++)
{
C[i][j] = (C[i - 1][j] + C[i-1][j - 1]) % mod;
}
}
3.单调栈本题做法
每个数(的下标)都入栈,当栈顶数小于当前数的时候,为栈顶数记录同时pop
期望的定义:
(大学概率论有)
将期望转化成概率求和的原理涉及到概率论中的期望值的定义。期望值是随机变量的加权平均值,而这个加权是根据每个可能结果发生的概率来进行的。
具体来说,对于一个离散型随机变量X,其期望值E(X)可以表示为:
E(X) = Σ x * P(X=x)
其中,x表示随机变量X可能取到的值,P(X=x)表示X取到值x的概率。
对于一个连续型随机变量X,其期望值E(X)可以表示为:
E(X) = ∫ x * f(x) dx
其中,f(x)表示X的概率密度函数。
因此,将期望转化成概率求和的原理就是根据每个可能结果发生的概率,将每个结果乘以对应的概率,然后将所有结果的加权平均值作为期望值。
本题期望:
“请你对每个容器求出它存活轮数的期望”
E = 求和 :存活i轮*存活i轮的概率
然而许多题解给出了这样的式子,
其实就是上式的转化
(我在此忏悔,这个式子不是我推的,我来第一步都没有列,脑子硬想,,)
样例解释:
对于输入#1有两种情况:
先碰3 1,然后碰2
或者先碰1 2,然后碰3
每种情况,3都能活2回合,2都能活1回合,1都是第一次就碎掉。
对于输入#2也是两种:
先碰12再碰3 1活0,2活1,3活2
或者先碰23再碰1 1活1,2活0,3活2
按定义的期望计算:
1: 活一次 1/2
2:活一次1/2
求逆元即使输出:
代码:
int C[105][105] = { 0 };
ll ksm(int x, int y) {
if (x == 1) return 1;
ll res = 1, base = x;
while (y) {
if (y & 1) res = (res * base) % mod;
base = (base * base) % mod;
y >>= 1;
}
return res;
}
int inv(int aim)//inverse element
{
return ksm(aim, mod - 2);
}
void solve(int c)
{
int n;
cin >> n;
vector<int>arr(n + 2);
vector<int>left(n + 2);
vector<int>right(n + 2);
stack<int>st1,st2;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
}
//“单调栈,为栈内右边最近的大的,所以现存栈内都是逆序小的,遇到大的挨着出栈
for (int i = 1; i <= n; i++)
{
while (st1.size() && arr[i] > arr[st1.top()])
{
right[st1.top()] = i-st1.top();
st1.pop();
}
st1.push(i);
}
for (int i = n; i >= 1; i--)
{
while (st2.size() && arr[i] > arr[st2.top()])
{
left[st2.top()] = st2.top()-i;
st2.pop();
}
st2.push(i);
}
//组合数处理:
C[0][0] = 1;
for (int i = 1; i <= n; i++)
{
C[i][0] = 1;
for (int j = 1; j <= i; j++)
{
C[i][j] = (C[i - 1][j] + C[i-1][j - 1]) % mod;
}
}
for (int i = 1; i <= n; i++)
{
int ans = 0;
for (int j = 1; j < n; j++)//轮数
{
int la = 0, lb = 0, lab = 0;
if (left[i] && j >= left[i])
{
//la = C[n-1-left[i]][j-left[i]] / C[n-1][j];
la = C[n-1-left[i]][j-left[i]] * inv( C[n-1][j])%mod;
}
if (right[i] && j >= right[i])
{
lb = C[n - 1 - right[i]][j - right[i]] * inv(C[n - 1][j]) % mod;
}
if (left[i] && right[i] && j >= left[i] + right[i])
{
lab = C[n-1-left[i]-right[i]][j-left[i]-right[i]] *inv( C[n - 1][j])
%mod;
}
ans = (ans + 1 + mod - la + mod - lb + lab) % mod;
}
cout << ans << " ";
}
}
signed main()
{
//ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t = 1;
//cin >> t;
for (int i = 1; i <= t; i++)
{
solve(i);
}
return 0;
}