2021牛客多校第一场 I——Increasing Subsequence

题目大意

给你一个 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+cidp[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+c1dp[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+c2dp[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/ndp[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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值