2021牛客多校#1 I-Increasing Subsequence

题目链接

链接

             https://ac.nowcoder.com/acm/contest/11166/I

题目大意

Alice和Bob在一个大小为 n ( 1 ≤ n ≤ 5000 ) n(1\leq n\leq 5000) n(1n5000) 的排列 P P P上玩游戏,双方轮流选择数字。

每一轮中,当前玩家需要选择一个比之前双方选择过的元素都大的元素。并且若该玩家之前选择了 P i P_i Pi,当前回合选择了 P j P_j Pj,则必须满足 j > i j>i j>i 。若有多种选择,则将等概率的随机选择一个合法的元素。

第一回合的玩家会随机选择一个元素,第二回合会随机选择一个比第一回合选择的数字大的元素。没有合法操作时结束游戏。
求游戏结束时,双方的总游戏回合数期望,答案对 998244353 998244353 998244353取模。

题解

这题需要运用期望问题和DP的算法,逆元可以通过预处理快速幂来计算。因为 1 ≤ n ≤ 5000 1 \leq n \leq 5000 1n5000 所以可以跑二重循环,判断是非符合条件进行计算。
由题意得DP转移式定义为第一维度表示我方上个选择的数,第二维度表示对方上个选择的数,即可得转移式为
p x < p y p_x<p_y px<py
d p [ x ] [ y ] = 1 + 1 c ∑ k > x   p k > p y d p k , y dp[x][y]=1+\tfrac{1}{c}\sum\limits_{k>x \ p_k>p_y}dp_{k,y} dp[x][y]=1+c1k>x pk>pydpk,y
p x > p y p_x>p_y px>py
d p [ x ] [ y ] = 1 + 1 c ∑ k > y   p k > p x d p x , k dp[x][y]=1+\tfrac{1}{c}\sum\limits_{k>y \ p_k>p_x}dp_{x,k} dp[x][y]=1+c1k>y pk>pxdpx,k
(k为所选数,下方为条件)
此时可以统计当前的前缀方便计算。
s u m y = ∑ k > x   p k > p y d p k , y sum_y=\sum\limits_{k>x \ p_k>p_y}dp_{k,y} sumy=k>x pk>pydpk,y
再采用 c n t i cnt_i cnti表示选择 p i p_i pi后可能选择的方案数
c n t y = ∑ k > x   p k > p y 1 cnt_y=\sum\limits_{k>x \ p_k>p_y}1 cnty=k>x pk>py1
快速幂的求解运用乘法逆元,即mod+2,故取模时要取模 m o d + 2 = 998244353 mod+2=998244353 mod+2=998244353
最后将概率乘上次数即可。胡言乱语

参考代码

#include<bits/stdc++.h>
using namespace std;
const long long N=5e3+5;
const long long mod=998244353;
int n,f[N];
long long dp[N][N],pop[N],cnt[N],sum[N];
long long powmod(long long x,long long p)    //快速幂预处理
{
    long long ret=1;
    while(p)
    {
        if(p&1)
            ret=ret*x%mod;
        x=x*x%mod;
        p>>=1;
    }
    return ret;
}
void read(long long &x){
	long long ret=0;
	char c=getchar(),last;
	while(c<'0'||c>'9')last=c,c=getchar();
	while(c>='0'&&c<='9'){
		ret=ret*10+c-'0';
		c=getchar();
	}
	x=last=='-'?-ret:ret;
}
int main()
{
    pop[0]=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    	pop[i]=powmod(i,mod-2);
    for(int i=1;i<=n;i++)
        scanf("%d",&f[i]);
    for(int i=n;i>=1;i--)                     
    {
    	long long s=0,c=0;
        for(int j=n;j>=0;j--)
        {
            if(i==j)
            	continue;
            if(f[i]>f[j])
            {
                dp[i][j]=(s*pop[c]+1)%mod;
                sum[j]+=dp[i][j];
                sum[j]%=mod;
                cnt[j]++;
            }
            else
            {
                dp[i][j]=(sum[j]*pop[cnt[j]]+1)%mod;
                s+=dp[i][j];
                s%=mod;
                c++;
            }
        }
    }
    long long ans=0;
    for(int i=1;i<=n;i++)
    	ans=(ans+dp[i][0])%mod;
    ans=ans*pop[n]%mod;                    //计算
    printf("%lld\n",ans);
}

总结

这道题大概是提高组C题的难度,将快速幂和DP+期望结合起来,值得一刷。有一定难度。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值