一、题目
二、解法
当 x + y + z x+y+z x+y+z 不是 3 3 3的倍数的时候无解。
一看就是
d
p
dp
dp 的题了,先把暴力一下的写出来,设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 为这个状态到分配完成的期望轮数(
i
=
z
−
x
,
j
=
z
−
y
i=z-x,j=z-y
i=z−x,j=z−y,
z
z
z最大),也就是我们用一个差分的形式来表示状态,转移:
d
p
[
a
]
[
b
]
=
1
2
d
p
[
a
−
2
]
[
b
−
1
]
+
1
2
d
p
[
a
−
2
]
[
b
−
1
]
+
1
dp[a][b]=\frac{1}{2}dp[a-2][b-1]+\frac{1}{2}dp[a-2][b-1]+1
dp[a][b]=21dp[a−2][b−1]+21dp[a−2][b−1]+1但是还没完,当
b
=
1
b=1
b=1 时转移是这样的:
d
p
[
a
]
[
1
]
=
d
p
[
a
−
2
]
[
0
]
+
2
dp[a][1]=dp[a-2][0]+2
dp[a][1]=dp[a−2][0]+2这种转移是可以根据样例发现的,通过
2
2
2轮的期望就可以完成把最高的填到最矮的操作。当出现等高的情况转移也比较特殊,也就是
b
=
0
b=0
b=0 的情况是这样子的:
d
p
[
a
]
[
0
]
=
1
2
d
p
[
a
+
1
]
[
2
]
+
1
2
d
p
[
a
−
1
]
[
1
]
+
1
dp[a][0]=\frac{1}{2}dp[a+1][2]+\frac{1}{2}dp[a-1][1]+1
dp[a][0]=21dp[a+1][2]+21dp[a−1][1]+1发现转移有环,但肯定不能高斯消元来解决,但转移的环仅限于
b
=
0
/
1
b=0/1
b=0/1 的情况,我们看能不能用手动消元的方式消掉,因为
d
p
[
a
]
[
0
]
dp[a][0]
dp[a][0] 的方程中有
d
p
[
a
+
1
]
[
2
]
dp[a+1][2]
dp[a+1][2],那我们就从他入手消元:
2
d
p
[
a
]
[
0
]
=
d
p
[
a
−
1
]
[
1
]
+
d
p
[
a
]
[
0
]
+
1
2
+
d
p
[
a
−
1
]
[
1
]
+
1
2dp[a][0]=\frac{dp[a-1][1]+dp[a][0]+1}{2}+dp[a-1][1]+1
2dp[a][0]=2dp[a−1][1]+dp[a][0]+1+dp[a−1][1]+1
d
p
[
a
]
[
0
]
=
d
p
[
a
−
1
]
[
1
]
+
2
dp[a][0]=dp[a-1][1]+2
dp[a][0]=dp[a−1][1]+2
d
p
[
a
]
[
0
]
=
d
p
[
a
−
3
]
[
0
]
+
4
dp[a][0]=dp[a-3][0]+4
dp[a][0]=dp[a−3][0]+4推到这里不难看出:
d
p
[
a
]
[
0
]
=
4
3
a
d
p
[
a
]
[
1
]
=
4
a
−
2
3
dp[a][0]=\frac{4}{3}a\space\space\space dp[a][1]=\frac{4a-2}{3}
dp[a][0]=34a dp[a][1]=34a−2那么转移到这里就不存在环了,我们可以愉快的
O
(
n
2
)
O(n^2)
O(n2) 转移拿到
70
70
70 分。
最后 30 30 30 分需要观察 d p [ a ] [ b ] dp[a][b] dp[a][b] 转移的性质,发现他的过程就是 马走日 ,而我们知道马走日问题是可以用组合数计算的,那么这道题能不能引入组合数直接算答案呢?
明确思路,我们不转移了,直接算
d
p
[
a
]
[
0
]
dp[a][0]
dp[a][0] 和
d
p
[
a
]
[
1
]
dp[a][1]
dp[a][1] 对答案
d
p
[
x
]
[
y
]
dp[x][y]
dp[x][y] 的贡献,可以看下图:
我们就是要算系数,那么系数是 2 − k 2^{-k} 2−k 次方,对于底层点(也就是我们处理出来的 b = 0 / 1 b=0/1 b=0/1的点)的 d p dp dp 值贡献和常数贡献都可以用这个系数呈上方案数计算(常数贡献是 k × 2 − k k\times 2^{-k} k×2−k)
时间复杂度 O ( n ) O(n) O(n)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 1000005;
const int MOD = 998244353;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,x,y,ans,a[5],fac[M],inv[M],bin[M],f0[M],f1[M];
void init(int n)
{
fac[0]=inv[0]=inv[1]=bin[0]=1;
for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) fac[i]=i*fac[i-1]%MOD;
for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
for(int i=1;i<=n;i++) f0[i]=4*i/3;
for(int i=1;i<=n;i++) f1[i]=(4*i-2)/3;
for(int i=1;i<=n;i++) bin[i]=bin[i-1]*inv[2]%MOD;
}
int C(int n,int m)
{
if(n<m || n<0 || m<0) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
signed main()
{
init(1e6);
T=read();
while(T--)
{
a[0]=read();a[1]=read();a[2]=read();
sort(a,a+3);ans=0;
int p=a[2]-a[0],q=a[2]-a[1];
if((p+q)%3)
{
puts("-1");
continue;
}
for(int i=1;i<=max(p,q);i++)
{
if(i%3) continue;
//这是(i,0)的贡献
int x=p-i,y=q,st=(x+y)/3,tmp=0;
if(st>=0)
{
tmp=C(st,x-st)-C(st-1,x-st-1);//减去(i+2,1)的贡献
ans=(ans+(f0[i]+st)*tmp%MOD*bin[st])%MOD;
}
//这是(0,i)的贡献
x=p;y=q-i;st=(x+y)/3;
if(st>=0)
{
tmp=C(st,y-st)-C(st-1,y-st-1);//减去(1,i+1)的贡献
ans=(ans+(f0[i]+st)*tmp%MOD*bin[st])%MOD;
}
}
for(int i=1;i<=max(p,q);i++)
{
if(i%3!=2) continue;
//这是(i,1)的贡献
int x=p-i,y=q-1,st=(x+y)/3,tmp=0;
if(st>=0)
{
tmp=C(st,x-st);
ans=(ans+(f1[i]+st)*tmp%MOD*bin[st])%MOD;
}
//这是(1,i)的贡献
x=p-1;y=q-i;st=(x+y)/3;
if(st>=0)
{
tmp=C(st,y-st);
ans=(ans+(f1[i]+st)*tmp%MOD*bin[st])%MOD;
}
}
printf("%lld\n",(ans+MOD)%MOD);
}
}