游戏

游戏

题解

蛮简单的一道题。

很容易发现,这道题可以dp。
d p i , j dp_{i,j} dpi,j表示最大的数与第二大与第三大的数之间的差值为 i , j i,j i,j时走到结局的期望步数。

容易得到方程式,
x , y ≥ 2 x,y \geq 2 x,y2
d p 0 , x = d p 2 , x + 1 + d p 1 , x − 1 2 + 1 dp_{0,x}=\frac{dp_{2,x+1}+dp_{1,x-1}}{2}+1 dp0,x=2dp2,x+1+dp1,x1+1
d p 1 , x = d p 0 , x − 2 + d p 1 , x 2 + 1 dp_{1,x}=\frac{dp_{0,x-2}+dp_{1,x}}{2}+1 dp1,x=2dp0,x2+dp1,x+1
d p x , y = d p x − 1 , y − 2 + d p x − 2 , y − 1 2 + 1 dp_{x,y}=\frac{dp_{x-1,y-2}+dp_{x-2,y-1}}{2}+1 dpx,y=2dpx1,y2+dpx2,y1+1
如果直接通过高斯消元去解得话是30pts ,当然,你得常数很小

可以通过上面的方程式可以解得,
d p 1 , x = d p 0 , x − 2 + 2 , d p 0 , x = d p 0 , x − 3 + 4 dp_{1,x}=dp_{0,x-2}+2,dp_{0,x}=dp_{0,x-3}+4 dp1,x=dp0,x2+2,dp0,x=dp0,x3+4
此时 x + y x+y x+y是不断减小的,最后一定会减小到0,中间转移过程可以通过dfs记忆化来实现,时间复杂度 O ( P ( n ) ) O\left( P(n) \right) O(P(n))。此时的 P ( n ) P(n) P(n) n n n拆成3个数的拆法。
如果用map来动态记录 d p dp dp状态的下标的话是60pts,但如果直接开大点数组,暴力存的话可以达到70pts。

我们再观察一下上面的方程式,可以发现,对于上面的方程式,对于 d p x , y dp_{x,y} dpx,y的部分,最后都一定会变为一些 d p 1 , x 1 dp_{1,x1} dp1,x1 d p 0 , x 2 dp_{0,x2} dp0,x2的和。而它是通过减一与减二减下去的,我们可以通过一元二次方程去算它的解。
先枚举每个 x 1 x1 x1 x 2 x2 x2,对于每个 d p 1 , x 1 dp_{1,x1} dp1,x1与每个 d p 0 , x 2 dp_{0,x2} dp0,x2算出它的概率,乘上从其出发的期望步数即可。
总时间复杂度 O ( t ( x + y + z ) ) O\left( t(x+y+z)\right) O(t(x+y+z))

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
typedef long long LL;
const LL jzm=998244353;
const int inv2=499122177;
const int qpg=23333;
typedef pair<int,int> pii;
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;
}
int t,x,y,z,fac[MAXN],inv[MAXN],f[MAXN],iv2[MAXN];
map<int,int>mp;
int qkpow(int a,int s){
	int t=1;
	while(s){
		if(s&1)t=1ll*a*t%jzm;
		a=1ll*a*a%jzm;s>>=1;
	}
	return t;
}
void init(){
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=iv2[0]=1;iv2[1]=inv2;
	for(int i=2;i<1e6;i++){
		fac[i]=1ll*i*fac[i-1]%jzm;
		f[i]=1ll*(jzm-jzm/i)*f[jzm%i]%jzm;
		inv[i]=1ll*f[i]*inv[i-1]%jzm;
		iv2[i]=1ll*iv2[i-1]*inv2%jzm;
	}
}
int C(int x,int y){
	if(x<0||y<0||x<y)return 0;
	return 1ll*fac[x]*inv[y]%jzm*inv[x-y]%jzm;
}
int add(int x,int y){return x+y<jzm?x+y:x+y-jzm;}
int DP(int x,int y){
	int res=0;
	for(int i=2;i<=y;i+=3){
		int p=(2*x-2-y+i)/3,q=(2*y-2*i-x+1)/3;if(p<0||q<0)continue;
		res=add(res,1ll*C(p+q,p)*iv2[p+q]%jzm*((i-2)/3*4+2+p+q)%jzm);
	}
	for(int i=3;i<=y;i+=3){
		int p=(2*x-y+i)/3,q=(2*y-2*i-x)/3;if(p<0||q<0)continue;
		res=add(res,1ll*add(C(p+q,p),jzm-C(p+q-1,p))*iv2[p+q]%jzm*(i/3*4+p+q)%jzm);
	}
	return res;
}
signed main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	read(t);init(); 
	while(t--){
		read(x);read(y);read(z);if((x+y+z)%3){puts("-1");continue;}
		if(x>z)swap(x,z);if(y>z)swap(y,z);if(x>y)swap(x,y);
		printf("%d\n",add(DP(z-x,z-y),DP(z-y,z-x)));
	}
	return 0;
}
/*
f(0,x)=1/2(f(2,x+1)+f(1,x-1))+1
f(0,x)=1/2(1/2(f(1,x-1)+f(0,x))+1+f(1,x-1))+1
f(0,x)=f(0,x-3)+4
*/

谢谢!!!

posted @ 2022-06-15 20:34  StaroForgin  阅读(4)  评论(0)    收藏  举报  来源