Borrow HDU6829
题意:三个人分别有 x , y , z x,y,z x,y,z的金钱,每次由最多的那人随机给另外两人中的一人一块钱(若有两个人相同最多,则两个中随机一人给钱),即变成 x − 1 , y , z + 1 x-1,y,z+1 x−1,y,z+1或 x − 1 , y + 1 , z x-1,y+1,z x−1,y+1,z。求使三个人金钱相同的操作数期望。
令 d p [ i ] [ j ] dp[i][j] dp[i][j]为另外两人与最大值的差值为 i , j i,j i,j时的期望数。
i , j ≥ 2 , d p [ i ] [ j ] = 1 + 1 2 d p [ i − 1 ] [ j − 2 ] + 1 2 d p [ i − 2 ] [ j − 1 ] i,j \ge2, dp[i][j]=1+\frac{1}{2}dp[i-1][j-2]+\frac{1}{2}dp[i-2][j-1] i,j≥2,dp[i][j]=1+21dp[i−1][j−2]+21dp[i−2][j−1]
d p [ i ] [ 1 ] = 2 + d p [ i − 1 ] [ 0 ] dp[i][1]=2+dp[i-1][0] dp[i][1]=2+dp[i−1][0]
d p [ i ] [ 0 ] = 1 + 1 2 d p [ i − 1 ] [ 1 ] + 1 2 d p [ i + 1 ] [ 2 ] dp[i][0]=1+\frac{1}{2}dp[i-1][1]+\frac{1}{2}dp[i+1][2] dp[i][0]=1+21dp[i−1][1]+21dp[i+1][2]
又 ∵ d p [ i + 1 ] [ 2 ] = 1 + 1 2 d p [ i ] [ 0 ] + 1 2 d p [ i − 1 ] [ 1 ] \because dp[i+1][2]=1+\frac{1}{2}dp[i][0]+\frac{1}{2}dp[i-1][1] ∵dp[i+1][2]=1+21dp[i][0]+21dp[i−1][1]
∴ d p [ i ] [ 0 ] = 2 + d p [ i − 1 ] [ 1 ] \therefore dp[i][0]=2+dp[i-1][1] ∴dp[i][0]=2+dp[i−1][1]
先预处理所有 d p [ i ] [ 0 ] dp[i][0] dp[i][0]和 d p [ i ] [ 1 ] dp[i][1] dp[i][1]的值
若考虑所有的 d p [ i ] [ j ] dp[i][j] dp[i][j],是 O ( n 2 ) O(n^2) O(n2)的,肯定会 T L E TLE TLE
对于
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],递归直到表达式全部由
d
p
[
k
]
[
0
]
dp[k][0]
dp[k][0]或
d
p
[
k
]
[
1
]
dp[k][1]
dp[k][1]组成,过程相当于一个二叉树,如图:
用组合数求出 ( k , 0 / 1 ) (k,0/1) (k,0/1)最后出现的个数,注意树的每一层都会用附加的操作数,第 i i i层就是 i i i。
d x = i − k , d y = j − 0 或 j − 1 dx= i-k,dy = j-0或j-1 dx=i−k,dy=j−0或j−1,层数即操作数就是 c n t = ( d x + d y ) / 3 cnt=(dx+dy)/3 cnt=(dx+dy)/3
第一种操作是 i − 1 , y − 2 i-1,y-2 i−1,y−2,次数是 d x − c n t dx-cnt dx−cnt,第二种操作是 i − 2 , j − 1 i-2,j-1 i−2,j−1,次数是 d y − c n t dy-cnt dy−cnt
所以 ( k , 1 ) (k,1) (k,1)子节点对答案的贡献就是 1 2 c n t ( c n t a ) ( d p [ k ] [ 1 ] + c n t ) \frac{1}{2^{cnt}} {cnt \choose a} (dp[k][1]+cnt) 2cnt1(acnt)(dp[k][1]+cnt)
( k , 0 ) (k,0) (k,0)子节点的父节点必须是 ( k + 1 , 2 ) (k+1,2) (k+1,2),所以贡献是 1 2 c n t ( c n t − 1 a ) ( d p [ k ] [ 0 ] + c n t ) \frac{1}{2^{cnt}} {cnt-1 \choose a} (dp[k][0]+cnt) 2cnt1(acnt−1)(dp[k][0]+cnt)
A C AC AC代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int M = 998244353;
const int N = 1000005;
int inv[N], f[N], invf[N], dp[N][2];
void init()
{
inv[0] = inv[1] = f[0] = f[1] = invf[0] = invf[1] = 1;
for(int i=2;i<N;++i) {
f[i] = (LL)f[i-1] * i % M;
inv[i] = (M - M / i) * (LL)inv[M % i] % M;
invf[i] = (LL)invf[i-1] * inv[i] % M;
}
for(int i=2;i<N;++i) {
if(i%3==0) dp[i][0] = 2 + dp[i-1][1];
else if((i+1)%3==0) dp[i][1] = 2 + dp[i-2][0];
}
}
void Sort(int &x,int &y,int &z){
if(x>z) swap(x,z);
if(y>z) swap(y,z);
if(x>y) swap(x,y);
}
int fpow(int a,int b){
int res = 1;
for(;b;b>>=1, a = ((LL)a * a) % M)
if(b&1) res = ((LL)res * a ) % M;
return res;
}
int solve(int x, int y, int i, int j)
{
if((i + j) % 3) return 0;
int dx = x - i, dy = y - j;
if(dx <= 0 || dy <= 0) return 0;
if((dx + dy) % 3) return 0;
int cnt = (dx + dy) / 3; //操作数
int a = dx - cnt, b = dy - cnt; //a为x-2,y-1 b为x-1,y-2
if(a < 0 || b < 0) return 0;
int ans;
if(!j){
if(!b) return 0;
ans = fpow(inv[2], cnt) * (LL)f[cnt-1] % M * invf[a] % M * invf[b-1] % M * (dp[i][j] + cnt) % M;
}
else ans = fpow(inv[2], cnt) * (LL)f[cnt] % M * invf[a] % M * invf[b] % M * (dp[i][j] + cnt) % M;
return ans;
}
int main()
{
init();
int t;
cin>>t;
while(t--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Sort(x, y, z); //令x<=y<=z
x = z - x; y = z - y;
if((x + y) % 3) {printf("-1\n"); continue;}
if(y < 2) {printf("%d\n", dp[x][y]); continue;}
int ans = 0;
for(int i=2;i<x;i++) { //遍历所有可能的叶节点
if(i % 3 == 0) ans = (((LL)ans + solve(x,y,i,0)) % M + solve(y,x,i,0)) % M; //叶节点可能是(i,0)或(0,i)
else if((i + 1) % 3 == 0) ans = (((LL)ans + solve(x,y,i,1)) % M + solve(y,x,i,1)) % M;
}
printf("%d\n",ans);
}
}