题目大意
给你一个
n
n
n 个数的排列
P
P
P
两个人轮流在其中取数,按照以下规则
1、每个人取的数的下标大于之前自己所取数的下标
2、每个人取的数要大于之前两个人所取的数
问两个人所取数的个数的期望
解题思路
期望DP
定义
d
p
[
i
]
dp[i]
dp[i] 表示当前在位置
i
i
i 还需要
d
p
[
i
]
dp[i]
dp[i] 的代价到达终点
d
p
[
i
]
=
v
i
+
∑
c
i
∗
d
p
[
j
]
dp[i] = v_i + \sum c_i * dp[j]
dp[i]=vi+∑ci∗dp[j]
答案是
d
p
[
0
]
dp[0]
dp[0]
v
i
v_i
vi 是这一步的代价,
j
j
j 是后继状态,
c
i
c_i
ci 是从状态
i
i
i 到状态
j
j
j 的概率
在这个题中,
v
i
=
1
v_i = 1
vi=1 设
k
k
k 为后继状态的个数,
c
i
=
1
/
k
c_i = 1 / k
ci=1/k
由于是两个人在走,所以我们开二维的
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] ,表示第一个人在第
i
i
i 个数,第二个人在第
j
j
j 的数。由于我们要遵循第二个规则,那么我们只需要比较
a
i
,
a
j
a_i, a_j
ai,aj 的大小就知道这一步轮到了谁走(小的这一步走)
-
a
i
>
a
j
:
d
p
[
i
]
[
j
]
=
1
+
∑
c
1
∗
d
p
[
i
]
[
k
1
]
k
1
a_i > a_j: dp[i][j] = 1 + \sum c_1 * dp[i][k_1] \ k_1
ai>aj:dp[i][j]=1+∑c1∗dp[i][k1] k1 是
i
i
i 后继状态的下标
c 1 c_1 c1 是 i i i 后继状态的个数 -
a
i
<
a
j
:
d
p
[
i
]
[
j
]
=
1
+
∑
c
2
∗
d
p
[
k
2
]
[
j
]
k
2
a_i < a_j: dp[i][j] = 1 + \sum c_2 * dp[k_2][j] \ k_2
ai<aj:dp[i][j]=1+∑c2∗dp[k2][j] k2 是
j
j
j 后继状态的下标
c 2 c_2 c2 是 j j j 后继状态的个数
这样转移复杂度是
O
(
n
3
)
O(n^3)
O(n3),我们考虑一下如何优化
我们发现
d
p
[
i
]
[
k
1
]
d
p
[
k
2
]
[
j
]
dp[i][k_1] \ \ \ dp[k_2][j]
dp[i][k1] dp[k2][j] 是分别与
j
i
j \ i
j i 无关的,所以我们用一个后缀和来统计一下这个求和式子
设
f
[
i
]
=
∑
d
p
[
i
]
[
k
1
]
g
[
j
]
=
∑
d
p
[
k
2
]
[
j
]
f[i] = \sum dp[i][k_1] \ \ g[j] = \sum dp[k_2][j]
f[i]=∑dp[i][k1] g[j]=∑dp[k2][j] 对于
f
[
i
]
f[i]
f[i] 我们需要满足的条件是
k
1
>
i
&
&
a
[
k
1
]
>
a
[
i
]
k_1 > i \ \&\& \ a[k_1] > a[i]
k1>i && a[k1]>a[i] 我们发现,在
g
[
j
]
g[j]
g[j] 中,令
j
=
k
1
k
2
=
i
j = k_1 \ k_2 =i
j=k1 k2=i 恰好是满足这个条件的。
所以我们在算第二种情况时,将
f
[
i
]
f[i]
f[i] 加上
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],在算第一种情况时
g
[
i
]
g[i]
g[i] 加上
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]
最后统计答案,因为第一个人可以任意选,我们要求的是 1 / n ∗ ∑ d p [ i ] [ 0 ] 1/n * \sum dp[i][0] 1/n∗∑dp[i][0]
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 5e3 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n;
ll dp[MAXN][MAXN];
ll inv[MAXN], f[MAXN], g[MAXN];
ll c1[MAXN], c2[MAXN];
ll a[MAXN];
ll qpow(ll x, ll y){
ll ret = 1;
while(y){
if(y&1)
ret = 1ll * ret * x % mod;
x = 1ll * x * x % mod;
y >>= 1;
}
return ret;
}
void solve(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
inv[i] = qpow(i, mod-2);
}
for(int i = n; i >= 0; --i){
for(int j = n; j >= 0; --j){
if(a[i] > a[j]){
dp[i][j] = (1 + inv[c1[i]] * f[i] % mod) % mod;
g[j] = (g[j] + dp[i][j]) % mod;
c2[j]++;
}
else if(a[i] < a[j]){
dp[i][j] = (1 + inv[c2[j]] * g[j] % mod) % mod;
f[i] = (f[i] + dp[i][j]) % mod;
c1[i]++;
}
}
}
ll ans = 0;
for(int i = 1; i <= n; i++)
ans = (ans + inv[n] * dp[i][0] % mod) % mod;
cout << ans << endl;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T;
// cin >> T;
T = 1;
while(T--){
solve();
}
return 0;
}