[unknown]分赃

280 篇文章 1 订阅
220 篇文章 2 订阅

题目

题目描述
三个好伙伴分赃款!小朋友不要学。

一开始三个人分别有 x , y , z x,y,z x,y,z 元。每一轮中,如果 x = y = z x=y=z x=y=z 则三人拿钱跑路,否则,钱最多的人拿出 1 1 1 元钱,随机给另外的俩人中的一个。如果有两个人都是钱最多的,那么二人会石头剪刀布决定谁给钱。石头剪刀布的获胜概率应当被视为 1 2 \frac{1}{2} 21

现在,请你求出三人期望经过多少轮才会跑路,或者输出 − 1 -1 1 表示他们在被警察抓到之前根本不能分完钱——也就是这个过程会永远进行下去。

数据范围与提示
对于 70 % 70\% 70% 的数据,有 x + y + z ⩽ 5 × 1 0 3 x+y+z\leqslant 5\times 10^3 x+y+z5×103

对于 100 % 100\% 100% 的数据,有 x + y + z ⩽ 1 0 6 x+y+z\leqslant 10^6 x+y+z106这数据范围真友好。

思路

首先 d p \tt dp dp 是很容易想到的,明显可以只存差分数组。

鉴于我已看过题解,不妨设 z z z 为三者中最大的,将 a = z − x a=z-x a=zx b = z − y b=z-y b=zy 作为自变量,那么有转移

f ( a , b ) = f ( a − 2 , b − 1 ) + f ( a − 1 , b − 2 ) 2 + 1 ( a , b ⩾ 2 ) f(a,b)=\frac{f(a{\it-}2,b{\it-}1){\it+}f(a{\it-}1,b{\it-}2)}{2}+1\quad(a,b\geqslant 2) f(a,b)=2f(a2,b1)+f(a1,b2)+1(a,b2)

几个边界条件是,大小关系发生了变化的情况。

{ f ( a , 1 ) = f ( a − 2 , 0 ) + 2 ( a ⩾ 2 )    f ( a , 0 ) = f ( a − 1 , 1 ) + f ( a + 1 , 2 ) 2 + 1 ( a ⩾ 2 ) \begin{cases} f(a,1)=f(a{\it-}2,0)+2&(a\geqslant 2)\\ \;\\ f(a,0)=\frac{f(a-1,1)+f(a+1,2)}{2}+1&(a\geqslant 2)\\ \end{cases} f(a,1)=f(a2,0)+2f(a,0)=2f(a1,1)+f(a+1,2)+1(a2)(a2)

至于 f ( 1 , 1 ) , f ( 1 , 0 ) f(1,1),f(1,0) f(1,1),f(1,0) 呢?可以忽略,因为它们不可能到达 f ( 0 , 0 ) = 0 f(0,0)=0 f(0,0)=0 。如果你更细心些,你会想到 f ( 2 , 0 ) f(2,0) f(2,0) 也不存在。更一般地, f ( a , b ) f(a,b) f(a,b) 只在 a + b ≡ 0 ( m o d 3 ) a+b\equiv 0\pmod{3} a+b0(mod3) 时有定义。

我当时以为,这样 70 70 70 分就到手了。可是阴险的出题人在家门口埋了颗地雷——转移有环!比如 f ( a , 0 ) f(a,0) f(a,0) f ( a + 1 , 2 ) f(a+1,2) f(a+1,2) 就可以互相转移。

考虑手推几步,去掉环。显然 a , b ⩾ 2 a,b\geqslant 2 a,b2 的情况并不产生环,所以只考虑 b < 2 b<2 b<2 的情况。
2 × f ( a , 0 ) = f ( a − 1 , 1 ) + f ( a + 1 , 2 ) + 2 = f ( a − 1 , 1 ) + f ( a , 0 ) + f ( a − 1 , 1 ) 2 + 3 ⇒ f ( a , 0 ) = f ( a − 1 , 1 ) + 2 \begin{aligned} 2\times f(a,0)&=f(a-1,1)+f(a+1,2)+2\\ &=f(a-1,1)+\frac{f(a,0)+f(a-1,1)}{2}+3\\ \Rightarrow f(a,0)&=f(a-1,1)+2 \end{aligned}\\ 2×f(a,0)f(a,0)=f(a1,1)+f(a+1,2)+2=f(a1,1)+2f(a,0)+f(a1,1)+3=f(a1,1)+2

甚至可以得寸进尺、得陇望蜀,我们把 f ( a − 1 , 1 ) f(a-1,1) f(a1,1) 再拆开,得到
f ( a , 0 ) = f ( a − 3 , 0 ) + 4 ⇒ f ( a , 0 ) = 4 a 3 ( a   m o d   3 = 0 ) ⇒ f ( a , 1 ) = 4 a − 2 3 ( a   m o d   3 = 2 ) \begin{aligned} f(a,0)&=f(a-3,0)+4\\ \Rightarrow f(a,0)&=\frac{4a}{3}\quad(a\bmod 3=0)\\ \Rightarrow f(a,1)&=\frac{4a-2}{3}\quad(a\bmod 3=2) \end{aligned} f(a,0)f(a,0)f(a,1)=f(a3,0)+4=34a(amod3=0)=34a2(amod3=2)

至此,环已经不是问题。接下来,我们要向最后 30 30 30 分发起猛攻!

注意 a , b ≥ 2 a,b\ge 2 a,b2 时的递推式是很美的,很像 马走日字。我们知道,马走日字是可以用组合数直接算出走到另一个格子的方案数的。那么我们直接求出初始状态 f ( z 0 − x 0 , z 0 − y 0 ) f(z_0{\it-}x_0,z_0{\it-}y_0) f(z0x0,z0y0) 走到 f ( a , 0 / 1 ) f(a,0/1) f(a,0/1)系数 是多少就行了!——走到 f ( 0 / 1 , a ) f(0/1,a) f(0/1,a) 同理。

也就是说, f ( a , b ) f(a,b) f(a,b) 不断用递推式往下递归,最后必然得到 f ( i , 0 / 1 ) f(i,0/1) f(i,0/1) 的线性和。我们要做的只是算出系数。即 ( 1 2 ) s t e p ({1\over 2})^{step} (21)step 乘方案数。

可是递推式中还要加一个常数呢!这个也挺好算,可以考虑每个位置的 + 1 +1 +1 在最终值里面的系数。显然是 ( 1 2 ) s t e p (\frac12)^{step} (21)step 。而走到 f ( a , 0 / 1 ) f(a,0/1) f(a,0/1) 之前,每个点有两个子节点(两次向下递归),这两个子节点(子状态)恰好分别提供系数 ( 1 2 ) s t e p + 1 (\frac12)^{step+1} (21)step+1,也就是说,一个状态贡献的常数是子状态贡献常数的和

既然这样,我们可以考虑只计算叶子节点(图的终止节点)的常数贡献的系数。又是计算系数啊,你好烦啊。叶子节点的贡献是 ( 1 2 ) s t e p (\frac12)^{step} (21)step,一共有 s t e p step step 个节点会加上该点的贡献(因为叶子节点自身是没有常数贡献的),显然还需要乘路径数量。

复杂度 O [ max ⁡ ( x , y , z ) ] \mathcal O[\max(x,y,z)] O[max(x,y,z)] 。常数什么的我们从来不在乎的。

代码

小坑点:由于 f ( a , 1 ) f(a,1) f(a,1) 是单独算的,它不能为 f ( a − 2 , 0 ) f(a-2,0) f(a2,0) 提供系数。

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		(c == '-' ? f = -f : 0);
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int Mod = 998244353;
const int MaxN = 1000010;
const int_ inv2 = (Mod+1)>>1;

int_ jc[MaxN], inv[MaxN];
int bin[MaxN]; // bin[x] = 2^{-x}
void prepare(int n){
	bin[0] = 1; // 2^0 = 1
	for(int i=1; i<MaxN; ++i)
		bin[i] = (bin[i-1]*inv2)%Mod;
	
	jc[1] = inv[1] = 1;
	for(int i=2; i<=n; ++i){
		inv[i] = (Mod-Mod/i)*inv[Mod%i]%Mod;
		jc[i] = i*jc[i-1]%Mod;
	}
	for(int i=2; i<=n; ++i)
		inv[i] = inv[i-1]*inv[i]%Mod;
	inv[0] = jc[0] = 1;
}
int_ C(int n,int m){
	if(m < 0 || n < m) return 0;
	return jc[n]*inv[m]%Mod*inv[n-m]%Mod;
}

int solve(int x,int y){
	if((x+y)%3) return -1; // 无解
	if(x > y) swap(x,y); // 方便些
	if(x == 0) return (y/3)*4;
	if(x == 1) return (4*y-2)/3;
	int ans = 0; // 最终答案
	for(int i=y/3*3; i; i-=3){
		/* (i,0) 的贡献 */ ;
		int k = (x+y-i)/3; // 行走步数
		int_ c = C(k-1,y-k-1); // = C(k,y-k)-C(k-1,y-k); // 去掉从 (i+2,1) 到来的
		c = c*bin[k]%Mod; // 每次都会带上系数 1/2
		ans = (ans+c*(i/3)*4)%Mod; // dp 值的贡献
		ans = (ans+c*k)%Mod; // 常数的贡献
		/* (0,i) 的贡献 */ ;
		c = C(k-1,x-k-1); // = C(k,x-k)-C(k-1,x-k); // 去掉从 (1,i+2) 到来的
		c = c*bin[k]%Mod; // 每次都会带上系数 1/2
		ans = (ans+c*(i/3)*4)%Mod; // dp 值的贡献
		ans = (ans+c*k)%Mod;
	}
	for(int i=(y-2)/3*3+2; i>=0; i-=3){
		/* (i,1) 的贡献 */ ;
		int k = (x+y-i-1)/3; // 行走步数
		int_ c = C(k,y-k-1)*bin[k]%Mod;
		ans = (ans+c*(4*i-2)/3)%Mod; // dp 贡献
		ans = (ans+c*k)%Mod; // 常数贡献
		/* (1,i) 的贡献 */ ;
		c = C(k,x-k-1)*bin[k]%Mod; // 不需要减掉啥了
		ans = (ans+c*(4*i-2)/3)%Mod;
		ans = (ans+c*k)%Mod;
	}
	return ans;
}

int main(){
	prepare(MaxN-1); // don't forget it!
	int T = readint();
	for(int x,y,z; T; --T){
		x = readint(), y = readint();
		z = readint();
		if(x > z) swap(x,z); // 选择排序
		if(y > z) swap(y,z);
		printf("%d\n",solve(z-x,z-y));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值