[组合数学]LeetCode 2954 统计感冒序列的数目

  • 一开始,下标为 1 和 3 的小朋友可以被传染,因为他们分别挨着有感冒的小朋友 0 和 4 ,令下标为 1 的小朋友先被传染。
    然后,下标为 2 的小朋友挨着感冒的小朋友 1 ,下标为 3 的小朋友挨着感冒的小朋友 4 ,两位小朋友都可以被传染,令下标为 2 的小朋友被传染。
    最后,下标为 3 的小朋友被传染,因为他挨着感冒的小朋友 2 和 4 ,感冒序列为 [1,2,3] 。
  • 一开始,下标为 1 和 3 的小朋友可以被传染,因为他们分别挨着感冒的小朋友 0 和 4 ,令下标为 1 的小朋友先被传染。
    然后,下标为 2 的小朋友挨着感冒的小朋友 1 ,下标为 3 的小朋友挨着感冒的小朋友 4 ,两位小朋友都可以被传染,令下标为 3 的小朋友被传染。
    最后,下标为 2 的小朋友被传染,因为他挨着感冒的小朋友 1 和 3 ,感冒序列为 [1,3,2] 。
  • 感冒序列 [3,1,2] ,被传染的顺序:[0,1,2,3,4] => [0,1,2,3,4] => [0,1,2,3,4] => [0,1,2,3,4] 。
  • 感冒序列 [3,2,1] ,被传染的顺序:[0,1,2,3,4] => [0,1,2,3,4] => [0,1,2,3,4] => [0,1,2,3,4] 。
    示例 2:
    输入:n = 4, sick = [1]
    输出:3
    解释:一开始,下标为 0 ,2 和 3 的小朋友没有感冒。总共有 3 个可能的感冒序列:
  • 感冒序列 [0,2,3] ,被传染的顺序:[0,1,2,3] => [0,1,2,3] => [0,1,2,3] => [0,1,2,3] 。
  • 感冒序列 [2,0,3] ,被传染的顺序:[0,1,2,3] => [0,1,2,3] => [0,1,2,3] => [0,1,2,3] 。
  • 感冒序列 [2,3,0] ,被传染的顺序:[0,1,2,3] => [0,1,2,3] => [0,1,2,3] => [0,1,2,3] 。
    参数范围
    2 <= n <= 105
    1 <= sick.length <= n - 1
    0 <= sick[i] <= n - 1
    sick 按升序排列。

分析

分段处理

因为病毒遇到已经感冒的小朋友会停止,所以未被感染的小孩,只会有被两个小孩感染直接或间接感染:
一,他左边,从右到左第一个初始感染的。
二,他右边,从左到右第一个初始感染的。
可以将其分成若干段。

各段处理

假定初始未感染的小孩数为m,如果无任何限制,则共有m! 种可能。m!是m的阶乘。
假定某段有len个小孩:
则此段被计算了len! 次。
实际可能数,如下表:

左边的那段1
中间的段2len-1
右边的那段1

除掉各段被计算的次数,乘以实际的可能数。

长度为0的段的处理

必须忽略,否则可能出错。无论是那一段长度为0和长度1的结果一样。所以可能将长度为0转化成成长度为1。

代码

复用代码

template
class C1097Int
{
public:
C1097Int(long long llData = 0) :m_iData(llData% MOD)
{

}
C1097Int  operator+(const C1097Int& o)const
{
	return C1097Int(((long long)m_iData + o.m_iData) % MOD);
}
C1097Int& operator+=(const C1097Int& o)
{
	m_iData = ((long long)m_iData + o.m_iData) % MOD;
	return *this;
}
C1097Int& operator-=(const C1097Int& o)
{
	m_iData = (m_iData + MOD - o.m_iData) % MOD;
	return *this;
}
C1097Int  operator-(const C1097Int& o)
{
	return C1097Int((m_iData + MOD - o.m_iData) % MOD);
}
C1097Int  operator*(const C1097Int& o)const
{
	return((long long)m_iData * o.m_iData) % MOD;
}
C1097Int& operator*=(const C1097Int& o)
{
	m_iData = ((long long)m_iData * o.m_iData) % MOD;
	return *this;
}
bool operator<(const C1097Int& o)const
{
	return m_iData < o.m_iData;
}
C1097Int pow(long long n)const
{
	C1097Int iRet = 1, iCur = *this;
	while (n)
	{
		if (n & 1)
		{
			iRet *= iCur;
		}
		iCur *= iCur;
		n >>= 1;
	}
	return iRet;
}
C1097Int PowNegative1()const
{
	return pow(MOD - 2);
}
int ToInt()const
{
	return m_iData;
}

private:
int m_iData = 0;;
};

核心代码

class Solution {
public:
	int numberOfSequence(int n, vector<int>& sick) {
		const int m = n - sick.size();
		vector<C1097Int<>> vFac(1,1);//记录[0,m]的阶乘


### 给大家的福利


**零基础入门**


对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。


![](https://img-blog.csdnimg.cn/img_convert/95608e9062782d28f4f04f821405d99a.png)


同时每个成长路线对应的板块都有配套的视频提供:


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a91b9e8100834e9291cfcf1695d8cd42.png#pic_center)


因篇幅有限,仅展示部分资料
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值