SP19148【INS14G - Kill them All】

前置知识:组合数 乘法逆元

感觉其他博客讲的不是很清楚,也没有说组合数公式是怎么来的,我这样数论极菜的萌新看了好久才想明白qwq。。

还是先给出本题组合数公式C(n-1,n/2)

分析一

转化成坐标系上的移动方案问题

把第一个人杀的怪物看成横坐标,第二个人的看成纵坐标,怪物由第一个人杀向右走,反之向上走。

可知第一步一定向右,枚举终点的纵坐标m(即向上的步数),题目就变成从点(1,0)开始走到(n-m,m)不越过y=x的方案数。枚举向上步数m,总方案数为C(n-1,m)(n-1步中选m步),现在我们要减去非法方案数。

从(1,0)走到(n-m,m)的不合法方案按y=x翻转后与从(0,1)走到(n-m,m)的方案相对应

注意这里的翻转,终点是不翻转的,只翻转起点。

不合法方案为\(C_{n-1}^{m-1}\)(n-1步中向上走了m-1步),由此可知答案为\(C_{n-1}^m-C_{n-1}^{m-1}\)

公式(用i枚举终点纵坐标,0步没有不合法方案特判,0≤m(第二个人杀的次数)≤n/2):

\(C_{n-1}^0+\sum\limits_{i=1}^{\left\lfloor\frac{n}{2}\right\rfloor}(C_{n-1}^{i}-C_{n-1}^{i-1})=\sum\limits_{i=1}^{\left\lfloor\frac{n}{2}\right\rfloor}C_{n-1}^i-\sum\limits_{i=1}^{\left\lfloor\frac{n}{2}\right\rfloor-1}C_{n-1}^{i}=C_{n-1}^{\left\lfloor\frac{n}{2}\right\rfloor}\)(公式来自@Alpha)

得组合数公式C(n-1,n/2)

(感谢@Alpha提供的图例与分析!)

分析二

建议可以先看看关于卡特兰数的博客中对卡特兰数的应用,和这题很像,便于理解

把题目放到笛卡尔坐标系(平面直角坐标系)中考虑(如图1),x轴表示每个怪兽,从原点出发向右上方走表示由D杀了,向右下方走表示由S杀了。相当于我从原点出发,我的轨迹不能在过程中碰到x轴。

图1

可知第一步一定向上,所以原题转化为从原点出发,轨迹在过程中不能跑到x轴下面。

枚举向上步数m,总方案数为C(n-1,m),现在我们要减去非法方案数。对于非法的轨迹,有两种翻转方式:

  1. 把它第一次碰到y=-1前的轨迹按y=-1翻转(见P1641 [SCOI2010]生成字符串),也就是从(0,-2)到达原先的终点的方案与不合法方案一一对应,会发现向上走的步数比翻转前多1,即得C(n,m+1)

  2. 把它第一次碰到y=-1后的轨迹按y=-1翻转(如图2),在n,m确定的情况下,终点是确定的,翻转后的轨迹和原轨迹的方案一一对应。那么终点在哪里呢? 设p为翻转后终点纵坐标,m为翻转前的向上步数,则有m - (n - m) + p = -2 (n-m为向下走的步数,翻转前终点纵坐标+1=翻转后纵坐标的相反数-1)-> p = -2 + n - 2m ,设当向上走x步时可以到达p,则 x - (n - x) = -2 + n - 2m -> x = n - m - 1,由于翻转后的轨迹和原轨迹的方案一一对应,所以到达p的方案数就是不合法的方案数

所以非法方案数为C(n,n-m-1)=C(n,m+1),(见组合数中的互补性质,即从m个不同元素中取出n个元素的组合数=从m个不同元素中取出(m-n)个元素的组合数)

枚举向上步数为m,总方案数为C(n,m)-C(n,m+1);

公式(n已减1,n-1步没有不合法方案特判,n/2≤m(第一个人杀的次数)≤n-1):

\(C_{n-1}^{n-1}+\sum\limits_{i=\left\lfloor\frac{n}{2}\right\rfloor}^{n-2}(C_{n-1}^{i}-C_{n-1}^{i+1})=\sum\limits_{i=\left\lfloor\frac{n}{2}\right\rfloor}^{n-1}C_{n-1}^i-\sum\limits_{i=\left\lfloor\frac{n}{2}\right\rfloor+1}^{n-1}C_{n-1}^{i}=C_{n-1}^{\left\lfloor\frac{n}{2}\right\rfloor}\)(公式来自@Alpha)

答案为C(n-1,n/2)

图2

(以上思路由这个博客改进而来)

代码1

fac[i]存1~i的阶乘

inv[i]存1~i逆元的乘积

#include<cstdio>
using namespace std;
#define p 1000000007
long long fac[1000005],inv[1000005];
inline long long C(int n,int m) {
if (n==m || m==0) return 1;
return fac[n]*inv[m]%p*inv[n-m]%p;
}
int main() {
int T,n;
scanf("%d",&T),fac[0]=inv[0]=inv[1]=1;
for (int i=1; i<1000005; i++) fac[i]=fac[i-1]*i%p;
for (int i=2; i<1000005; i++) inv[i]=p-(p/i)*inv[p%i]%p;
for (int i=2; i<1000005; i++) inv[i]=inv[i-1]*inv[i]%p;
while(T--)
scanf("%d",&n),printf("%lld\n",C(n-1,n/2));
}

代码2

#include<cstdio>
using namespace std;
#define p 1000000007
#define int long long
int fac[1000005];
inline int pow(int x) {
int ans=1;
x%=p;
for (int i=p-2; i; i>>=1,x=x*x%p)
if (i&1) ans=ans*x%p;
return ans;
}//快速幂求逆元(x^(p-2)) signed main() {
int T,n;
scanf("%lld",&T),fac[0]=1;
for (int i=1; i<=1000005; i++) fac[i]=fac[i-1]*i%p;
while(T--)
scanf("%lld",&n),printf("%lld\n", fac[n-1]*pow(fac[n/2]*fac[n-1-n/2]%p)%p);
}

通过这题的确对组合数的应用加深了不少,如果分析有误还望指出qwq!

参考文章:

最后再次感谢@Alpha的耐心讲解与图例公式_

题解 SP19148【INS14G - Kill them All】的更多相关文章

  1. 「题解」:Kill

    问题 A: Kill 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...

  2. CSP-S 模拟测试 45 题解

    由于咕掉的题解太多了,所以只能趁改完不动题的时间,来补补坑qwq,还是太弱了. 考试过程: 到新机房的第一次考试,貌似海星? 第一题一开始就觉得是个贪心,但以为所有小怪兽都要打完,所以想复杂了,但后来 ...

  3. 2014年亚洲区域赛北京赛区现场赛A,D,H,I,K题解(hdu5112,5115,5119,5220,5122)

    转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud 下午在HDU上打了一下今年北京区域赛的重现,过了5题,看来单挑只能拿拿铜牌,呜呜. ...

  4. LeetCode All in One题解汇总(持续更新中...)

    突然很想刷刷题,LeetCode是一个不错的选择,忽略了输入输出,更好的突出了算法,省去了不少时间. dalao们发现了任何错误,或是代码无法通过,或是有更好的解法,或是有任何疑问和建议的话,可以在对 ...

  5. LeetCode Kill Process

    原题链接在这里:https://leetcode.com/problems/kill-process/description/ 题目: Given n processes, each process ...

  6. Leetcode 简略题解 - 共567题

    Leetcode 简略题解 - 共567题     写在开头:我作为一个老实人,一向非常反感骗赞.收智商税两种行为.前几天看到不止两三位用户说自己辛苦写了干货,结果收藏数是点赞数的三倍有余,感觉自己的 ...

  7. [CSP-S模拟测试97]题解

    A.小盆友的游戏 感觉题解解释的很牵强啊……还是打表找规律比较靠谱 对于每个人,它构造了一个期望函数$f(x)$,设它的跟班个数为$cnt[x]$,那么令$f(x)=2^{cnt[x]}-1$(??鬼 ...

  8. [CSP-S模拟测试45]题解

    开局一行$srand$,得分全靠随机化. A.kill 发现两个并不显然的性质: 1.选中的人和怪物一定是按顺序的.第一个人打所有被选中怪物的第一只,第二个人打第二只,$etc$. 2.最优方案打的怪 ...

  9. ORACLE快速彻底Kill掉的会话

    在ORACLE数据库当中,有时候会使用ALTER SYSTEM KILL SESSION 'sid,serial#'杀掉一个会话进程,但是使用这个SQL语句杀掉会话后,数据库并不会立即释放掉相关的资源 ...

随机推荐

  1. 163.扩展User模型-一对一方式扩展

    一对一外键 如果你对用户验证方法authenticate没有更多的要求,就是使用username和password就可以完成用户的登录验证工作,但是想要在原来的模型的基础上添加新的字段,那么就可以使用 ...

  2. Zookeeper学习笔记一

    目录 一.zookeeper介绍 二.Zookeeper安装 一.zookeeper介绍 1.概述   Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目.Hadoop ...

  3. set的使用

    集合是Python的一种数据类型,集合是一个可变容器.常用于列表的去重. 什么是集合 集合是一个可变容器 集合中的数据对象都是唯一的(不可重复) 集合是无序的存储结构 集合是可迭代对象 集合内的元素是 ...

  4. Django---Django初始

    现在测试行业慢慢的也需要存在代码能力了,从以前的点点点到通过工具进行测试接口,到目前的自动化测试,需要测试会代码的越来越多了.慢慢的测试开发这一行业出现在我们的面前,测试不仅仅会点点,还能自己帮助开发 ...

  5. 高级特征工程II

    以下是Coursera上的How to Win a Data Science Competition: Learn from Top Kagglers课程笔记. Statistics and dist ...

  6. SGD 讲解,梯度下降的做法,随机性。理解反向传播

    SGD 讲解,梯度下降的做法,随机性.理解反向传播 待办 Stochastic Gradient Descent 随机梯度下降没有用Random这个词,因为它不是完全的随机,而是服从一定的分布的,只是 ...

  7. 解决PHP Redis扩展无法加载的问题

    最近在工作中需要使用PHP访问Redis,从https://github.com/phpredis/phpredis下载了phpredis,并且按照官方的说明进行了安装 phpize ./config ...

  8. 【Python】获取星期字符串

    原理:字符串切片 1.0代码: #获取星期字符串 weekStr="星期一星期二星期三星期四星期五星期六星期日" weekId=eval(input("请输入星期数字(1 ...

  9. Oracle Data Guard搭建 1.虚拟机安装linux

    1.安装虚拟机 VMware 14 2.下载Linux镜像文件,创建虚拟机

  10. linux网卡

    手动启动 ifup eth0 查询网卡配置信息 vim /etc/udev/rules.d/70-persistent-net.rules 备注:可以修改网卡名称和MAC地址