[PKUSC2022]Rating

Rating

题解

可怜的我考场上拿了0分
首先看到这道题了,我们容易定义出一个比较简单的 d p dp dp,用 d p i , j dp_{i,j} dpi,j表示较大数为 i i i,较小的数为 j j j的概率。
本来定义到这里就可以了,我们将所有状态的概率直接加起来就可以得到答案了,这里为了方便我们再多定义一个 g p i , j gp_{i,j} gpi,j表示到达该状态的期望步数。
然后,我们可以简单地写出它的转移方程式:
p k d p i , j → d p i , j + k / d p i + k , j p k ( g p i , j + d p i , j ) → g p i , j + k / g p i + k , j p_kdp_{i,j}\rightarrow dp_{i,j+k}/dp_{i+k,j}\\ p_k(gp_{i,j}+dp_{i,j})\rightarrow gp_{i,j+k}/gp_{i+k,j} pkdpi,jdpi,j+k/dpi+k,jpk(gpi,j+dpi,j)gpi,j+k/gpi+k,j注意,第二个方程的位置必须是加上 d p i , j dp_{i,j} dpi,j,因为前面的 g p i , j gp_{i,j} gpi,j表示的是每种路径被使用的概率乘上每种路径的长度,扩展一步就是每种情况。
所以这里加上的应该是到达该点的概率,而不是 1 1 1
由于下一层的值是不会影响上一层的,我们可以先将每一层的 d p dp dp值消元解出后,再去解下一层的方程。
接着方程我们事实上最多也只能优化到 O ( m n 3 ) O\left(mn^3\right) O(mn3),看起来不太像能过的样子,我们考虑优化。

如果真要优化的,显然也就不能用列方程组的方法了。
我们可以考虑一些顺推的 d p dp dp,我们不妨再多定义一个 f i , j , g i , j f_{i,j},g_{i,j} fi,j,gi,j表示从 i i i出发走到第一个大于 i i i的点为 i + j i+j i+j的概率与期望花费。
考虑怎么将这东西快速地预处理出来,定义 h i , j , h p i , j h_{i,j},hp_{i,j} hi,j,hpi,j表示从 i i i出发后不再走大于 j j j的点第一次走到 j j j概率与期望距离。
我们先枚举从 i i i出发走的第一步 k k k,将它贡献到 h i , i + k h_{i,i+k} hi,i+k上,显然这个 i + k i+k i+k可能比 i i i小,也可能比 i i i大。
大丈夫です,对于比 i i i小的部分,它的 d p dp dp值不是已经预处理出来了吗,它就可以从比较小的 h j h_j hj贡献到较大的 h k ( k > j ) h_k(k>j) hk(k>j)
这样,我们就能将 < i <i <i出的 h h h值转移到 ⩾ i \geqslant i i处。
那么我们考虑,如何将这个 h h h算到我们的 f f f值上呢?显然, h i , i h_{i,i} hi,i相当于走了一个环,又走回到了 i i i上的概率与期望,而大于 i i i的部分会直接贡献到其它 f f f值的贡献。
可以计算出 f i , j f_{i,j} fi,j f i , k / f k , i ( k > j ) f_{i,k}/f_{k,i}(k>j) fi,k/fk,i(k>j)的贡献为 h k 1 − h i \frac{h_k}{1-h_i} 1hihk,对 g i , k / g k , i g_{i,k}/g_{k,i} gi,k/gk,i的贡献为 h p k ( 1 − h i ) + h p i h k ( 1 − h i ) 2 \frac{hp_k(1-h_i)+hp_ih_{k}}{(1-h_i)^2} (1hi)2hpk(1hi)+hpihk
这样我们就可以在 O ( n m 2 ) O\left(nm^2\right) O(nm2)的时间内完成我们的预处理。

有了上面的 f f f g g g,我们自然而然就很容易得到 d p dp dp的转移方法。
显然,我们只需要枚举较小的数下一个变到的比他大的数是什么就行了。
这部分的转移就可以做到 O ( n m 2 ) O\left(nm^2\right) O(nm2)

总时间复杂度 O ( n m 2 ) O\left(nm^2\right) O(nm2)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1055
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double Ld;
typedef pair<int,int> pii;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int mod=1e6+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=100;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0)putchar('-'),print(-x);if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,val[105],g[MAXN][55],f[MAXN][55],ans;
int dp[MAXN][MAXN],gp[MAXN][MAXN],h[MAXN],hp[MAXN];
int main(){
	read(n);read(m);int summ=0,iv8=qkpow(100000000,mo-2,mo);
	for(int i=0;i<=m+m;i++)read(val[i]),val[i]=1ll*iv8*val[i]%mo;
	for(int i=0;i<=m;i++)Add(summ,val[i],mo);
	dp[0][0]=1;gp[0][0]=0;
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m+m;j++)
			Add(h[max(i+j-m,0)],val[j],mo),
			Add(hp[max(i+j-m,0)],val[j],mo);
		for(int j=max(0,i-m);j<i;j++)
			for(int k=1;k<=m;k++)
				Add(h[j+k],1ll*h[j]*f[j][k]%mo,mo),
				Add(hp[j+k],1ll*hp[j]*f[j][k]%mo,mo),
				Add(hp[j+k],1ll*h[j]*g[j][k]%mo,mo);
		const int tp=qkpow(add(1,mo-h[i],mo),mo-2,mo);
		for(int j=1;j<=m;j++)
			f[i][j]=1ll*h[i+j]*tp%mo,
			g[i][j]=1ll*add(hp[i+j],1ll*hp[i]*f[i][j]%mo,mo)*tp%mo;
		for(int j=max(0,i-m);j<=i+m;j++)h[j]=hp[j]=0;
	}
	for(int i=0;i<n;i++)
		for(int j=max(i-m,0);j<=i;j++){
			for(int k=1;k<=m;k++)
				if(j+k<=i)
					Add(dp[i][j+k],1ll*dp[i][j]*f[j][k]%mo,mo),
					Add(gp[i][j+k],1ll*dp[i][j]*g[j][k]%mo,mo),
					Add(gp[i][j+k],1ll*gp[i][j]*f[j][k]%mo,mo);
				else if(j+k<n)
					Add(dp[j+k][i],1ll*dp[i][j]*f[j][k]%mo,mo),
					Add(gp[j+k][i],1ll*dp[i][j]*g[j][k]%mo,mo),
					Add(gp[j+k][i],1ll*gp[i][j]*f[j][k]%mo,mo);
				else if(j+k>=n)
					Add(ans,1ll*dp[i][j]*g[j][k]%mo,mo),
					Add(ans,1ll*gp[i][j]*f[j][k]%mo,mo);
		}
	printf("%d\n",ans);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值