游戏
游戏
题解
蛮简单的一道题。
很容易发现,这道题可以dp。
设
d
p
i
,
j
dp_{i,j}
dpi,j表示最大的数与第二大与第三大的数之间的差值为
i
,
j
i,j
i,j时走到结局的期望步数。
容易得到方程式,
当
x
,
y
≥
2
x,y \geq 2
x,y≥2时
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,x−1+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,x−2+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=2dpx−1,y−2+dpx−2,y−1+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,x−2+2,dp0,x=dp0,x−3+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
*/

浙公网安备 33010602011771号