[usOJ6166]坏天平

说在前面

这篇博客给了我思路与代码,里面有三道题,而且讲述的逻辑非常骚气

所以我把这道题拿了出来,单独讲,逻辑清楚些。

题目

传送门 to usOJ

题目描述
我是盗图狗

数据范围与约定
对于 30 % 30\% 30% 的数据, n k ≤ 12 nk\le 12 nk12

对于另外 20 % 20\% 20% 的数据, m = 2 , k = 1 m=2,k=1 m=2,k=1

对于另外另外 20 % 20\% 20% 的数据, m = 3 , k = 2 m=3,k=2 m=3,k=2

对于 100 % 100\% 100% 的数据, 1 ≤ n k ≤ 1 0 7 , k < m ≤ 1 0 7 1\le nk\le 10^7,k<m\le 10^7 1nk107,k<m107

思路

等价转换

看看数据中的一个奇怪的限制: k < m k<m k<m(而且还跟 m ≤ 1 0 7 m\le 10^7 m107 隐藏在一起),出题人好坏

众所周知, ∑ i = 0 x − 1 m i = m x − 1 m − 1 \sum_{i=0}^{x-1}m^i=\frac{m^x-1}{m-1} i=0x1mi=m1mx1;结合 k ≤ m − 1 k\le m-1 km1 这一条,得到了非常骇人的结论,那就是 k ∑ i = 0 x − 1 m i ≤ m x − 1 k\sum_{i=0}^{x-1}m^i\le m^x-1 ki=0x1mimx1

这就是在说,对于一个砝码 m x m^x mx ,比它轻的 k x kx kx 个砝码加在一起,也没有 m x m^x mx 重。

所以,看似限制条件 R x − L x < max ⁡ R_x-L_x<\max RxLx<max 很奇怪,其实本质就是这句话:

在任意时刻,对于已经放过的最重的砝码,左边的数量不小于右边的数量

简化情况

试着去处理 n = 1 n=1 n=1 的情况。不妨用 g ( x , y ) g(x,y) g(x,y) 表示左边有 x x x 个、右边有 y y y 个,方案数是多少。

也许你会试着去递推。但是方案数太多,不可能通过 k < 1 0 7 k<10^7 k<107 的数据。考虑用数学方法计算。

平面直角坐标系 中找到 g g g 的意义。在左边放一个,相当于向右走一个单位;在右边放一个,相当于向上走一个单位。将起点规定为 ( 0 , 0 ) (0,0) (0,0) ,那么终点就应该是 ( x , y ) (x,y) (x,y) 。这个限制,就相当于不能碰到 l : y = x + 1 l:y=x+1 l:y=x+1 这条直线。

在坐标系内行走……不能碰到某条直线……有 卡特兰数 内味了。

仿照卡特兰数的求解方法(什么?你是像作者一样直接背的结论?)即可——碰到警戒线就将其翻折。
在这里插入图片描述
如图,绿色(深绿 a n d and and 浅绿)的线是一条非法路径。将其第一次触碰到警戒线以后的部分(浅绿部分)翻折,得到了紫色的线。不难发现,这种情况就是走到上面的紫色点的路径数量(可以证明,到达上面紫色点的路径与非法路径一一对应)。

你别告诉我没限制的情况的方案数 ( x + y x ) {x+y\choose x} (xx+y) 你都不知道!因为一共要走 x + y x+y x+y 步,其中 x x x 步是向右走。

那么,对称之后的点是什么呢?是 ( y − 1 , x + 1 ) (y-1,x+1) (y1,x+1) 。这个是初中数学的范畴,我就不推了。

于是我们可以直接求出 g ( x , y ) = ( x + y x ) − ( x + y y − 1 ) g(x,y)={x+y\choose x}-{x+y\choose y-1} g(x,y)=(xx+y)(y1x+y)

所以,我们现在要求的就是 ∑ i = 0 ⌊ n 2 ⌋ g ( n − i , i ) \sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}g(n-i,i) i=02ng(ni,i)

根据刚才得出的结论, g ( n − i , i ) = ( n i ) − ( n i − 1 ) g(n-i,i)={n\choose i}-{n\choose{i-1}} g(ni,i)=(in)(i1n) ;将其代入,就有了骇人的结论:

a n s = ∑ i = 0 ⌊ n 2 ⌋ [ ( n i ) − ( n i − 1 ) ] = ( n ⌊ n 2 ⌋ ) ans =\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\left[{n\choose i}-{n\choose{i-1}}\right] ={n\choose\lfloor\frac{n}{2}\rfloor} ans=i=02n[(in)(i1n)]=(2nn)

好的!现在我们就 得到了0分的高分 解决了 n = 1 n=1 n=1 的情况。

所有情况

然后奇技淫巧出炉了:从重到轻的考虑每一种砝码
我是盗图狗*2
这是题解。我连题解都看不懂了,宣告提前退役

其实就是这样的,我们对最轻的砝码进行考虑,并转移。以下都是讨论最轻的那一种砝码的放置。

放在最前面的(数量可以枚举一下),由于只有它这一种,其方案数就应该是先前所求出来的 ( i ⌊ i / 2 ⌋ ) {i\choose\lfloor i/2\rfloor} (i/2i) (毕竟它就是当前最重,必须满足条件)。

后面就可以放飞自我了。想放哪里放哪里——它一定不是最重的砝码了。然后就是经典的隔板法了;最后乘上一个 2 2 2 的幂,表示放左边或右边。这里简单提一下隔板法;会的就可以直接跳过了。

隔板法简单介绍。

已经放了 k ( n − 1 ) k(n-1) k(n1) 个砝码,就有了 n k − k nk-k nkk 个间隔(不能放在最前方,一定是放在某个砝码之后)。我现在要将 k − i k-i ki 个砝码塞到这些间隔中;这将会把原有的 n k − k nk-k nkk 个砝码分割成 k − i + 1 k-i+1 ki+1 段。不妨设它们各自的数量为 x 1 , x 2 , x 3 , … , x k − i + 1 x_1,x_2,x_3,\dots,x_{k-i+1} x1,x2,x3,,xki+1 ,相当于求这个方程组的解: { ∑ j = 1 k − i + 1 x j = n k − k x 1 ≥ 1 , x y ( y ≠ 1 ) ≥ 0 \begin{cases}\sum_{j=1}^{k-i+1}x_j=nk-k\\x_1\ge 1,x_y(y\ne 1)\ge 0\end{cases} {j=1ki+1xj=nkkx11,xy(y=1)0

为什么 x 1 ≥ 1 x_1\ge 1 x11 呢?因为第一段不可能没有砝码(不会放在最前方)。其它的就无所谓了,非负即可。

于是我们将 x y ( y ≠ 1 ) x_y(y\ne 1) xy(y=1) 共同增大 1 1 1 ,现在要求这个方程的正整数解 x 1 + x 2 + x 3 + ⋯ + x k − i + 1 = n k − k + ( k − i ) = n k − i x_1+x_2+x_3+\cdots+x_{k-i+1}=nk-k+(k-i)=nk-i x1+x2+x3++xki+1=nkk+(ki)=nki

这就是经典的隔板法——将 k n − i kn-i kni 个小球划分成 k − i + 1 k-i+1 ki+1 组,相当于在 k n − i − 1 kn-i-1 kni1 个空隙中选 k − i k-i ki 个作为分割线。所以方案数就是 ( n k − i − 1 k − 1 ) {nk-i-1\choose{k-1}} (k1nki1) 。最后枚举一下,这 k − i k-i ki 个砝码究竟是放左边还是放右边,乘 2 k − i 2^{k-i} 2ki

隔板法很简单吧?

写出结论: a n s n = a n s n − 1 ∑ i = 0 k ( i ⌊ i 2 ⌋ ) ( n k − i − 1 k − i ) 2 k − i ans_{n}=ans_{n-1}\sum_{i=0}^{k}{i\choose\lfloor \frac{i}{2}\rfloor}{nk-i-1\choose{k-i}}2^{k-i} ansn=ansn1i=0k(2ii)(kinki1)2ki

递推解决战斗。时间复杂度 O ( n k ) \mathcal O(nk) O(nk)为什么我的组合数学之魂凌乱不堪

代码实现

这里有一个非常有意思的优化,众所周知,

( n m ) = n ! m ! ( n − m ) ! {n\choose m}=\frac{n!}{m!(n-m)!} (mn)=m!(nm)!n!

n − ⌊ n 2 ⌋ = ⌈ n 2 ⌉ n-\left\lfloor\frac{n}{2}\right\rfloor=\left\lceil\frac{n}{2}\right\rceil n2n=2n

所以就得到了这个结论, ( n ⌊ n 2 ⌋ ) = n ! ⌊ n 2 ⌋ ! ⌈ n 2 ⌉ ! {n\choose\left\lfloor\frac{n}{2}\right\rfloor}=\frac{n!}{\lfloor\frac{n}{2}\rfloor!\lceil\frac{n}{2}\rceil!} (2nn)=2n!2n!n!

将其记为 A A A 。考虑一下它与 ( n − 1 ) ! ⌊ n − 1 2 ⌋ ! ⌈ n − 1 2 ⌉ ! \frac{(n-1)!}{\lfloor\frac{n-1}{2}\rfloor!\lceil\frac{n-1}{2}\rceil!} 2n1!2n1!(n1)!

的关系。将其记为 B B B 。分类讨论一下。

  1. 如果 2 ∣ n 2|n 2n ,那么就有 ⌊ n − 1 2 ⌋ + 1 = ⌊ n 2 ⌋ , ⌈ n − 1 2 ⌉ = ⌈ n 2 ⌉ \lfloor\frac{n-1}{2}\rfloor+1=\lfloor\frac{n}{2}\rfloor,\lceil\frac{n-1}{2}\rceil=\lceil\frac{n}{2}\rceil 2n1+1=2n,2n1=2n ,所以推出:
    A B = n ⌊ n 2 ⌋ = n ⌊ n + 1 2 ⌋ \frac{A}{B}=\frac{n}{\lfloor\frac{n}{2}\rfloor}=\frac{n}{\lfloor\frac{n+1}{2}\rfloor} BA=2nn=2n+1n
  2. 如果 2 ∣ ( n + 1 ) 2|(n+1) 2(n+1) ,那么就有 ⌊ n − 1 2 ⌋ = ⌊ n 2 ⌋ , ⌈ n − 1 2 ⌉ + 1 = ⌈ n 2 ⌉ \lfloor\frac{n-1}{2}\rfloor=\lfloor\frac{n}{2}\rfloor,\lceil\frac{n-1}{2}\rceil+1=\lceil\frac{n}{2}\rceil 2n1=2n,2n1+1=2n ,所以推出:
    A B = n ⌈ n 2 ⌉ = n ⌊ n + 1 2 ⌋ \frac{A}{B}=\frac{n}{\lceil\frac{n}{2}\rceil}=\frac{n}{\lfloor\frac{n+1}{2}\rfloor} BA=2nn=2n+1n

然后就可以递推一发了。形式化地,若 f ( x ) = ( x ⌊ x 2 ⌋ ) f(x)={x\choose \lfloor\frac{x}{2}\rfloor} f(x)=(2xx) ,则

f ( x ) = x ⋅ f ( x − 1 ) ⌊ x + 1 2 ⌋ f(x)=\frac{x\cdot f(x-1)}{\lfloor\frac{x+1}{2}\rfloor} f(x)=2x+1xf(x1)

当然,我们要计算 2 k − i 2^{k-i} 2ki 次方,所以不妨将 i i i 从大到小进行枚举,就可以直接嘿嘿嘿了。

哦,对了,还有一个组合数没有处理……

∵ ( n k − i − 1 k − i ) = ( n k − i − 1 ) ! ( k − i ) ! ( n k − k − 1 ) ! (1) \because {nk-i-1\choose k-i}=\frac{(nk-i-1)!}{(k-i)!(nk-k-1)!}\tag{1} (kinki1)=(ki)!(nkk1)!(nki1)!(1)

a n d   ∵ ( n k − ( i + 1 ) − 1 k − ( i + 1 ) ) = [ n k − ( i + 1 ) − 1 ] ! [ k − ( i + 1 ) ] ! ( n k − k − 1 ) ! (2) and\space\because {nk-(i+1)-1\choose k-(i+1)}=\frac{\left[nk-(i+1)-1\right]!}{[k-(i+1)]!(nk-k-1)!}\tag{2} and (k(i+1)nk(i+1)1)=[k(i+1)]!(nkk1)![nk(i+1)1]!(2)

∴ ( 1 ) ( 2 ) = n k − i − 1 k − i \therefore \frac{(1)}{(2)}=\frac{nk-i-1}{k-i} (2)(1)=kinki1

所以跟 2 k − i 2^{k-i} 2ki 一起递推即可(共同存在一个临时变量里)。

#include<cstdio>

const int MAXN = 10002030, mod = 998244353;
int n, m, k, inv[MAXN], f[MAXN]; // f存储n=1的情况的答案
 
int main(){
    inv[0] = inv[1] = f[0] = 1;
    scanf("%d %d %d",&n,&m,&k);
    for(int i=2; i<=k; i++) // 线性筛逆元
    	inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1; i<=k; i++) // 递推求组合数
    	f[i]=1ll*f[i-1]*i%mod*inv[(i+1)>>1]%mod;
    long long ans = f[k]; // ans_1的值
    for(int i=2; i<=n; i++){
        long long t = 0, c = 1; // c是临时变量;t是求和结果
        for(int j=k; j>=0; --j){
        	 // 枚举前面放了几个
        	if(j != k) // 递推组合数2
        		c = (c<<1)*(i*k-j-1)%mod*inv[k-j]%mod;
        	t = (t+1ll*c*f[j]%mod)%mod;
        }
        ans = ans*t%mod; // 乘上ans_{i-1}
    }
    printf("%d\n",int(ans));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值