QBXT游记 | Day3 Moring

组合数求模

也就是求 C(n,m)%p=?

Lv.1

其中n,m<=2000,p为任意整数

根据题意大约能看出是\(O(n^2)\)的算法可求

我们知道C(n,m)=[C(n-1,m-1)+C(n-1,m)]%p

所以我们能知道这个数最大能到达杨辉三角的第2000行,可以直接预处理的时候把每个数%p即可

Lv.2

其中n,m<=106,p>=109且为质数

根据定义柿:C(n,m)%p=\(\frac{n!}{n!(n-m)!}\)%p=n!%p x (m!)-1%p x (n-m)-1%p

因为p是质数,所以对于这个柿子,任何数的逆元一定存在

Lv.3

其中n,m<=106,p<=2000且为质数

因为n,m都可能比p大,所以刚才那个做法里面的逆元不存在

这里引入一个新的定理:卢卡斯定理

对于n和m,已知p为质数,n和m都能表示为p进制:\(n=n_k+n_{k-1}+\cdots+n_1\),m同理(这里要保证位数能一致,不够的加上前导0

内容:参见OIwiki,经过lucas分解之后就能得到一个p进制的形式,那样的话逆元是存在的,就可以用Lv2的方法求了

LV.4

其中n,m<=106,p=p1*p2,p1和p2都<=2000且为质数

能知道C(n,m)%p1=a1,C(n,m)%p2=a2(利用刚才的方法)

可以使用连理方程组后Excrt求解

x%p1=a1

x%p2=a2

👇

x%p1p2=a3

Lv.4 Pro

把p扩展至p=p1p2p3p4...pk,pi<=2000

一样对每个质数使用Lv3的方法取模,然后用Excrt合并求解

当然有些更加的离谱的数据是处理不了的

组合数例题

P1

Describe

  • 要求你把x拆成k个不同的组合数之和
  • 只要C(n1 n2)或者C(m1 m2) 不同,就叫做不同的组合数
  • 任意输出一种方案
    • x<=109,k<=103

Solution

x=1+1+1+1+1+1+1+1+1...+1+1+(x-k+1)(一共有k-1个1)

=C(0,0)+C(1,0)+C(2,0)...+C(k-2,0)+C(x-k+1,1) (C(n,1)=n)

代码如下(洛谷P4369,noip.ac的数学3的第一题)

#include <bits/stdc++.h>
#define Heriko return
#define Deltana 0
#define S signed
#define U unsigned
#define LL long long
#define R register
#define I inline
#define D double
#define LD long double
#define mst(a,b) memset(a,b,sizeof(a))
#define ON std::ios::sync_with_stdio(false)
using namespace std;
I void fr(LL &x)
{
	LL f=1;char c=getchar();
	x=0;
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	x*=f;
}
void fw(LL x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9) fw(x/10);
	putchar('0'+x%10);
}
S main()
{
	LL x,k;
	fr(x),fr(k);
	for(R LL i=1;i<k;i++) fw(i),putchar(' '),fw(0),putchar('\n');
	fw(x-k+1);putchar(' ');fw(1);putchar('\n');
	Heriko Deltana;
}

别看代码挺长,实际上是我缺省源比较长

P2

比较C(n1,m1),C(n2,m2)的大小关系

如果C(n1,m1)<C(n2,m2),那么我们可以推出log C(n1,m1)<log C(n2,m2)

因为这两者都大于1,所以可以直接根据对数函数来推出这柿子

这里用一个res来存储log i的阶乘,Code大体如下

	for(R LL i=1;i<=n;i++) res[i]=res[i-1]+log[i];
//res[i]=log[i]!
	log[C[n][m]]=res[n]-res[n-m]-res[m]; 

P2 Pro

Describe

  • 找到k个不同的组合数使得这k个组合数的和最大
  • 其中 C(a,b) 满足 0<=b<=a<=n
  • n<=106 k<=105

Solution

k=1时,我们观察杨辉三角可以发现,每行最大的组合数都在最后一行的中间,即为 C(n,n/2)

k=2时,我们可以发现次大的组合数一定在最大的组合数周围(也就是说一定在最大的左上右上(奇数行)/正上(偶数行)/左右)

我们考虑维护一个堆(使用优先队列priority_queue)

找到最大的扔进去,然后把他周围的数都扔进堆

然后每次取出最大的,把它的周围扔进去,就可以求出前k大的组合数,然后求和即可

大约的就是P2加上一个堆

P3

Describe

\[\left(\sum_{i=0}^\infty C_{nk}^{ik+r}\right)\ mod\ p \]

Solution

C(n,m)=C(n-1,m-1)+C(n-1,m)=C(n-2,m-2)+2C(n-2,m-1)+C(n-2,m)

	  =C(n-3,m-3)+3C(n-3,m-2)+...

	  =1...+4...+6...+4...+1...

实际上可以发现系数也是组合数

抽屉原理

把n+1个物体放到n个抽屉里面,则至少有一个抽屉含有两个或两个以上的物品

P4

Describe

  • 给定N个数,要求选择任意多个数,使他们的和为c的倍数
  • c<=N<=105

Solution

发现c可能等于N

把抽屉编号为0,1,2,3,4,5,6,...,c-1

每个抽屉对应的是 和%c=i

用sum[i]来代表前i个数的和,我们能算出0~n的前缀和,所以一共有n+1个sum

如果sum[i]%c=k,那么我们把他放到抽屉里面,此时是有n+1个物品,c个抽屉

然后根据抽屉原理,一定能找到某个抽屉里面有两个sum(这两个前缀和一定不相同),那么这2个前缀和相减就是我们要求的区间

P5

Describe

  • 平面上有N个点,要求用三个边长为L的正方形覆盖全部的点(每个正方形都是横平竖直的),求最小的L
  • N<= 5*104

Solution

先考虑能否用三个L x L 的正方形能否覆盖所有点

那么肯定是最边界的点(x最大的,y最大的,x最小的,y最小的)会对L影响最大

三个正方形,四个点,那么说明只要有一个正方形放角上(要覆盖两个点)

那么先暴力枚举让那个边角上的满足条件,然后再去枚举其他的正方形去在覆盖剩下的2个极点,然后再去暴力枚举能覆盖所有点的边长

复杂度是\(O(4n\log n)\)

容斥原理

以下是我上课手打的一个非\(\LaTeX\)的柿子,非常的乱

|A1∪A2∪A3∪...An|=|A1|+|A2|+...+|An|-|A1∩A2|-|A2∩A3|-...-|An-1 ∩ An|+|A1∩A2∩A3|+|A2∩A3∩A4|+...+|An-2 ∩ An-1 ∩ An|...

并且这个柿子非常简陋,建议看OI Wiki上的柿子

P6

Describe

有n个夫妻,他们坐成一圈,要求一对夫妻之间不能相邻

Solution

强制绑定一对夫妻相邻

(2N-1)! - C(N,1) x (2N-2)! x 2 + C(N,2) x (2N-3)! x 22-C(N,3) x (2N-1)! x 23 ....

=\(\sum\limits_{i=0}^N (-1)^i*C(N,i)*(2N-i-1)!*2!\)

实际上这两部分差不多的,下面的是课堂笔记,拿出来作为一个参考

实际上这两部分差不多的原因是我懒得改了

上课Craft笔记部分:

组合数求模

C(n,m)%p=?

Lv.1

其中n,m<=2000,p为任意整数

根据题意大约能看出是\(O(n^2)\)的算法可求

我们知道C(n,m)=[C(n-1,m-1)+C(n-1,m)]%p

所以我们能知道这个数最大能到达杨辉三角的第2000行,可以直接预处理的时候把每个数%p即可

Lv.2

其中n,m<=106,p>=109且为质数

根据定义柿:C(n,m)%p=\(\frac{n!}{n!(n-m)!}\)%p=n!%p x (m!)-1%p x (n-m)-1%p

因为p是质数,所以对于这个柿子,任何数的逆元一定存在

Lv.3

其中n,m<=106,p<=2000且为质数

因为n,m都可能比p大,所以刚才那个做法里面的逆元不存在

这里引入一个新的定理:卢卡斯定理

对于n和m,已知p为质数,n和m都能表示为p进制:\(n=n_k+n_{k-1}+\cdots+n_1\),m同理(这里要保证位数能一致,不够的加上前导0

内容:参见OIwiki,经过lucas分解之后就能得到一个p进制的形式,那样的话逆元是存在的,就可以用Lv2的方法求了

LV.4

其中n,m<=106,p=p1*p2,p1和p2都<=2000且为质数

能知道C(n,m)%p1=a1,C(n,m)%p2=a2(利用刚才的方法)

可以使用连理方程组后Excrt求解

x%p1=a1

x%p2=a2

👇

x%p1p2=a3

Lv.4 Pro

把p扩展至p=p1p2p3p4...pk,pi<=2000

一样对每个质数使用Lv3的方法取模,然后用Excrt合并求解

当然有些更加的离谱的数据是处理不了的

组合数例题

P1

Describe

  • 要求你把x拆成k个不同的组合数之和
  • 只要C(n1 n2)或者C(m1 m2) 不同,就叫做不同的组合数
  • 任意输出一种方案
    • x<=109,k<=103

Solution

x=1+1+1+1+1+1+1+1+1...+1+1+(x-k+1)(一共有k-1个1)

=C(0,0)+C(1,0)+C(2,0)...+C(k-2,0)+C(x-k+1,1) (C(n,1)=n)

代码如下(洛谷P4369,noip.ac的数学3的第一题)

#include <bits/stdc++.h>
#define Heriko return
#define Deltana 0
#define S signed
#define U unsigned
#define LL long long
#define R register
#define I inline
#define D double
#define LD long double
#define mst(a,b) memset(a,b,sizeof(a))
#define ON std::ios::sync_with_stdio(false)
using namespace std;
I void fr(LL &x)
{
	LL f=1;char c=getchar();
	x=0;
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	x*=f;
}
void fw(LL x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9) fw(x/10);
	putchar('0'+x%10);
}
S main()
{
	LL x,k;
	fr(x),fr(k);
	for(R LL i=1;i<k;i++) fw(i),putchar(' '),fw(0),putchar('\n');
	fw(x-k+1);putchar(' ');fw(1);putchar('\n');
	Heriko Deltana;
}

别看代码挺长,实际上是我缺省源比较长

P2

比较C(n1,m1),C(n2,m2)的大小关系

如果C(n1,m1)<C(n2,m2),那么我们可以推出log C(n1,m1)<log C(n2,m2)

因为这两者都大于1,所以可以直接根据对数函数来推出这柿子

这里用一个res来存储log i的阶乘,Code大体如下

	for(R LL i=1;i<=n;i++) res[i]=res[i-1]+log[i];
//res[i]=log[i]!
	log[C[n][m]]=res[n]-res[n-m]-res[m]; 

P2 Pro

Describe
  • 找到k个不同的组合数使得这k个组合数的和最大
  • 其中 C(a,b) 满足 0<=b<=a<=n
  • n<=106 k<=105
Solution

k=1时,我们观察杨辉三角可以发现,每行最大的组合数都在最后一行的中间,即为 C(n,n/2)

k=2时,我们可以发现次大的组合数一定在最大的组合数周围(也就是说一定在最大的左上右上(奇数行)/正上(偶数行)/左右)

我们考虑维护一个堆(使用优先队列priority_queue)

找到最大的扔进去,然后把他周围的数都扔进堆

然后每次取出最大的,把它的周围扔进去,就可以求出前k大的组合数,然后求和即可

大约的就是P2加上一个堆

P3

Describe

\[\left(\sum_{i=0}^\infty C_{nk}^{ik+r}\right)\ mod\ p \]

Solution

C(n,m)=C(n-1,m-1)+C(n-1,m)=C(n-2,m-2)+2C(n-2,m-1)+C(n-2,m)

​ =C(n-3,m-3)+3C(n-3,m-2)+...

​ =1...+4...+6...+4...+1...

实际上可以发现系数也是组合数

抽屉原理

把n+1个物体放到n个抽屉里面,则至少有一个抽屉含有两个或两个以上的物品

P4

Describe

  • 给定N个数,要求选择任意多个数,使他们的和为c的倍数
  • c<=N<=105

Solution

发现c可能等于N

把抽屉编号为0,1,2,3,4,5,6,...,c-1

每个抽屉对应的是 和%c=i

用sum[i]来代表前i个数的和,我们能算出0~n的前缀和,所以一共有n+1个sum

如果sum[i]%c=k,那么我们把他放到抽屉里面,此时是有n+1个物品,c个抽屉

然后根据抽屉原理,一定能找到某个抽屉里面有两个sum(这两个前缀和一定不相同),那么这2个前缀和相减就是我们要求的区间

P5

Describe

  • 平面上有N个点,要求用三个边长为L的正方形覆盖全部的点(每个正方形都是横平竖直的),求最小的L
  • N<= 5*104

Solution

先考虑能否用三个L x L 的正方形能否覆盖所有点

那么肯定是最边界的点(x最大的,y最大的,x最小的,y最小的)会对L影响最大

三个正方形,四个点,那么说明只要有一个正方形放角上(要覆盖两个点)

那么先暴力枚举让那个边角上的满足条件,然后再去枚举其他的正方形去在覆盖剩下的2个极点,然后再去暴力枚举能覆盖所有点的边长

复杂度是\(O(4n\log n)\)

容斥原理

|A1∪A2∪A3∪...An|=|A1|+|A2|+...+|An|-|A1∩A2|-|A2∩A3|-...-|An-1 ∩ An|+|A1∩A2∩A3|+|A2∩A3∩A4|+...+|An-2 ∩ An-1 ∩ An|...

P6

Describe

有n个夫妻,他们坐成一圈,要求一对夫妻之间不能相邻

Solution

强制绑定一对夫妻相邻

(2N-1)! - C(N,1) x (2N-2)! x 2 + C(N,2) x (2N-3)! x 22-C(N,3) x (2N-1)! x 23 ....

=\(\sum\limits_{i=0}^N (-1)^i*C(N,i)*(2N-i-1)!*2!\)

posted @ 2021-05-03 11:25  HerikoDeltana  阅读(80)  评论(1编辑  收藏  举报