Borrow HDU6829

Borrow HDU6829

题意:三个人分别有 x , y , z x,y,z x,y,z的金钱,每次由最多的那人随机给另外两人中的一人一块钱(若有两个人相同最多,则两个中随机一人给钱),即变成 x − 1 , y , z + 1 x-1,y,z+1 x1,y,z+1 x − 1 , y + 1 , z x-1,y+1,z x1,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,j2,dp[i][j]=1+21dp[i1][j2]+21dp[i2][j1]

d p [ i ] [ 1 ] = 2 + d p [ i − 1 ] [ 0 ] dp[i][1]=2+dp[i-1][0] dp[i][1]=2+dp[i1][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[i1][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[i1][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[i1][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=ik,dy=j0j1,层数即操作数就是 c n t = ( d x + d y ) / 3 cnt=(dx+dy)/3 cnt=(dx+dy)/3

第一种操作是 i − 1 , y − 2 i-1,y-2 i1,y2,次数是 d x − c n t dx-cnt dxcnt,第二种操作是 i − 2 , j − 1 i-2,j-1 i2,j1,次数是 d y − c n t dy-cnt dycnt

所以 ( 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(acnt1)(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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值