排列组合、crt、费马小定理,容斥原理,lucas定理,组合数取模

一些公式&定理

\(lucas\)定理

\(C_n^m \equiv C_{n/p}^{m/p}\times C_{n\%p}^{m\%p}(mod\;p)\)

代码


int lucas(int n,int m,int p){
	if(m>n)return 0;
	if(m==0)return 1;
	if(n<p&&m<p)return 1ll*jc[n]*inv[m]%p*inv[n-m]%p;
	return lucas(n/p,m/p,p)*lucas(n%p,m%p,p)%p;
}

错排

\(f[n]=(n-1)*(f[n-1]+f[n-2])\)

考虑加入一个元素\(n\),通过一步操作得到错排

  • \(n-1\)个数是错排,\(n\)随便交换一个,方案数\((n-1)*f[n-1]\)

  • \(n-1\)个数有一个在原位置,共\(n-1\)种可能,剩下的数构成错排\(f[n-2]\)\(n\)只能和它交换,方案数\((n-1)*f[n-2]\)

  • \(n-1\)个数有两个或以上个数在原位置,一步无法得到错排

加起来即可

crt 中国剩余定理

求解

\(\begin{cases} x &\equiv a_1 \pmod {n_1} \\ x &\equiv a_2 \pmod {n_2} \\ &\vdots \\ x &\equiv a_k \pmod {n_k} \\ \end{cases}\)

其中\(n_i\)两两互质

  • 计算\(P\),为所有\(n_i\)的乘积

  • 计算\(c_i=P/n_i\),以及\(c_i^-1(mod\;n_i)\)

  • 答案\(x=\sum_{i=1}^k a_ic_i c_i^{-1} \pmod P\)

证明,\(x\%n_i时,其他的因为c_j的存在一定\equiv0,只有a_i留下,并且c_ic_i^{-1}\equiv 1\)所以得到了需要的答案

严谨过程

image

code
#include<cstdio>
#include<cstring>

using namespace std;

typedef long long ll;

ll a[15],p[15],m[15],inv[15];

ll slow(ll x,ll y,ll mod){
	ll ans=0;
	for(;y;y>>=1,x=(x+x)%mod)if(y&1)ans=(ans+x)%mod;
	return ans;
}

ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return gcd;
}

ll INV(ll a,ll b){
	ll x,y;
	ll gcd=exgcd(a,b,x,y);
	return (x%b+b)%b;
}

int main()
{
	int n;scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%lld %lld",&p[i],&a[i]);
	ll P=1;for(int i=1;i<=n;++i)P*=p[i];
	for(int i=1;i<=n;++i)m[i]=P/p[i];
	for(int i=1;i<=n;++i)inv[i]=INV(m[i],p[i]);
	ll x=0;
	for(int i=1;i<=n;++i)x=(x+slow(slow(a[i],m[i],P),inv[i],P))%P;
	printf("%lld\n",x);
	return 0;
}

excrt

使用扩展欧几里得算法两两合并

\(\begin{cases} x &\equiv a_1 \pmod {n_1} \\ x &\equiv a_2 \pmod {n_2} \\ \end{cases}\)

\(x=a_1+pn_1=a_2-qn_2\)

\(pn_1+qn_2=a_2-a_1(mod\;lcm)\)

code
#include<cstdio>
#include<cstring>

using namespace std;

typedef long long ll;

ll res[100005],p[100005];


ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return gcd;
}


ll china(int n){
	ll a,b,c,x,y,gcd,ans=res[1];
	a=p[1];
	for(int i=2;i<=n;++i){
		b=p[i];c=res[i]-ans;
		int gcd=exgcd(a,b,x,y);
		if(c%gcd!=0)return -1;
		x=(x*(c/gcd)%(b/gcd)+(b/gcd))%(b/gcd);
		ans=x*a+ans;
		a=a/gcd*b;
	}
	return ans;
}

int main()
{
	int n;
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;++i)scanf("%lld %lld",&p[i],&res[i]);
		if(n==1){printf("%lld\n",res[1]);continue;}
		printf("%lld\n",china(n));
	}
	return 0;
}

exlucas

跟lucas没啥关系。。

根据唯一分解定定理

\(p={q_1}^{\alpha_1}\cdot{q_2}^{\alpha_2}\cdots{q_r}^{\alpha_r}\)

求解

\(\left\{ \begin{aligned} a_1\equiv \displaystyle\binom{n}{m}&\pmod {{q_1}^{\alpha_1}}\\ a_2\equiv \displaystyle\binom{n}{m}&\pmod {{q_2}^{\alpha_2}}\\ &\cdots\\ a_r\equiv \displaystyle\binom{n}{m}&\pmod {{q_r}^{\alpha_r}}\\ \end{aligned} \right.\)

就可以使用crt合并

现在考虑计算

\(\displaystyle a_i=\binom{n}{m}\bmod {q_i}^{\alpha_i}\)

无法直接计算,如果移除因子\(q\)最后统一计算呢

\(\frac{\frac{n!}{q^x}}{\frac{m!}{q^y}\frac{(n-m)!}{q^z}}q^{x-y-z} \bmod q^k\)

现在考虑求

\(\frac{n!}{q^x}\bmod q^k\)

image

显然现在就是求这东西

反过来,展开有

\(n! = q^{\left\lfloor\frac{n}{q}\right\rfloor} \cdot \left(\left\lfloor\frac{n}{q}\right\rfloor\right)! \cdot {\left(\prod_{i,(i,q)=1}^{q^k}i\right)}^{\left\lfloor\frac{n}{q^k}\right\rfloor} \cdot \left(\prod_{i,(i,q)=1}^{n\bmod q^k}i\right)\)

后面那两个奇怪的东西就是\((n!)_p\),只不过分成了两部分

为什么可以这样分?

因为\(a\equiv a+q^k(mod\;q^k)\)

\(q^{\left\lfloor\frac{n}{q}\right\rfloor}\)这东西不用求

\(\left(\left\lfloor\frac{n}{q}\right\rfloor\right)!\)递归求解

剩下那个暴力就行

code
#include<cstdio>
#include<cstring>

using namespace std;

typedef long long ll;

ll w[105];

ll qpow(ll x,ll y,ll mod){
	ll ans=1;
	for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)ans=1ll*ans*x%mod;
	return ans;
}

ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return gcd;
}

ll inv(ll a,ll p){
	ll x,y;
	ll gcd=exgcd(a,p,x,y);
	return (x%p+p)%p;
}
ll get_f(ll n,ll p,ll pk){
	if(!n)return 1;
	ll res=1,ls=n%pk;
	for(ll i=2;i<=ls;++i)if(i%p)res=1ll*res*i%pk;
	ll num=res;
	for(ll i=ls+1;i<pk;++i)if(i%p)res=1ll*res*i%pk;
	return 1ll*get_f(n/p,p,pk)*qpow(res,n/pk,pk)%pk*num%pk;
}


ll get_c(ll n,ll m,ll p,ll pk){
	ll res=0,d=n-m;
	for(ll i=p;i<=n;i*=p)res+=n/i;
	for(ll i=p;i<=m;i*=p)res-=m/i;
	for(ll i=p;i<=d;i*=p)res-=d/i;
	return 1ll*qpow(p,res,pk)*get_f(n,p,pk)%pk*inv(get_f(m,p,pk),pk)%pk*inv(get_f(d,p,pk),pk)%pk;
}
ll exlucas(ll n,ll m,ll p){
	if(m==n)return 1;
	ll lp=p;ll ans=0;
	for(ll i=2;i*i<=lp;++i){
		if(lp%i)continue;
		ll pk=1;while(lp%i==0){lp/=i,pk*=i;}
		ll c=p/pk;
		ans=(ans+1ll*get_c(n,m,i,pk)*c%p*inv(c,pk)%p)%p;
	}
	if(lp!=1){
		ll c=p/lp;
		ans=(ans+1ll*get_c(n,m,lp,lp)*c%p*inv(c,lp)%p)%p;
	}
	return ans;
}

int main(){
	ll mod,n,m;
	scanf("%lld%lld%lld",&mod,&n,&m);
	for(ll i=1;i<=m;++i)scanf("%lld",&w[i]);
	ll sum=0;for(ll i=1;i<=m;++i)sum+=w[i];
	if(sum>n)printf("Impossible\n");
	else{
		ll ans=exlucas(n,sum,mod);
		for(ll i=1;i<=m;++i){
			ans=1ll*ans*exlucas(sum,w[i],mod)%mod;
			sum-=w[i];
		}
		printf("%lld\n",ans%mod);
	}
	return 0;
}

题目乱写

A. 排队

两种情况

  • 老师中间是男生

男生排列\(A_n^n\)

老师插空\(A_{n+1}^2\)

女生插空\(A_{n+3}^m\)

  • 老师中间是女生

男生排列\(A_n^n\)

老师+一个女生整体法\(C_{n+1}^1*A_2^2\)

剩下女生插空\(A_{n+2}^{m-1}\)

打个高精

B. combination

\(lucas\)模板,关于\(lucas\)的证明我没看明白,先不管了

C. 排列计数

基本是错排板子,加一个简单组合数就行了

D. Perm 排列计数

把大小关系画出来,发现是个二叉堆,然后组合数+树DP

E. 集合计数

容斥

\(f[i]\)表示交集至少\(i\)个的方案数

首先钦定这\(i\)个交集\(c_n^i\)

剩下的\(n-k\)个数能构成\(2^{n-k}\)个集合,每个集合都可以选或不选,去掉空集\(2^{2^{n-k}}-1\)

然后容斥一下就行了

F. 牡牛和牝牛

简单DP,有排列组合做法,但没必要

G BZOJ 4403序列统计

首先\(l,r\)是假的,只有\(r-l\)有用

然后枚举长度,隔板法处理

H DZY Loves Math II

题意转化\(R-L\)个不同元素,取\(1-N\)个元素,有多少种取法,直接组合数就行
x=
然后问题就是一个背包了...吗?

发现值域太大,完全跑不出来

但是可以发现

\(n=k*S+a\)

注意\(a\)不一定小于\(S\)

分成两部分求解

\(k*S\),每个\(S\)可以由\(S/p_i\)\(p_i\)构成,每个\(p_i\)都有可能,用隔板法就是\(c_{k+cnt-1}^{cnt-1}\)

\(a\)这部分直接背包就行,但是注意由于上面的计算,这里需要保证每个\(p_i\)使用次数小于\(S/p_i\)

多重背包?其实不用,完全背包再加一个\(dp[i]-=dp[i-s]\)就行了

I. 虔诚的墓主人

这题我觉得是扫描线,但是严重怀疑不是正解,然后,,,就是扫描线,加上一个简单的组合数就行

J. 地精部落

妙极了,不会做的蓝题系列

首先看性质

  1. 在一个数列中,若\(i\)\(i+1\)不相邻,直接交换这两个数字就可以组成一个新的数列
  2. 把波动数列中的每个数字\(a_i\)变成\((n+1)-a_i\)会得到互补的数列,即且新数列的山峰与山谷情况相反
  3. 数列有对称性

状态设计

\(dp[i][j]\)表示选择\(1-i\)这些数字,第一位是\(j\)并且是山峰的方案数

转移

因为性质\(1\)\(dp[i][j]+=dp[i][j-1]\)(\(dp[i][j-1]\)中,因为\(j-1\)是峰,\(j\)一定不与\(j-1\)相邻)

如果\(j\)\(j-1\)相邻,那么把\(j\)放到以\(j-1\)为开头并且\(j-1\)是谷的数列前,因为性质2,那么有\(dp[i][j]+=dp[i-1][i+1-j]\)

答案

因为性质3,答案就是\(\sum_{i=1}^n dp[n][i]*2\)

K. 看电影

这题应该放到高精题单里!

假设存在一个\(k+1\)号座位,然后把座位\(k+1\)与座位\(1\)连上,这样每个人都有座位,合法的方案里第\(k+1\)个座位应该是没有人的,枚举哪个位置是\(k+1\)断开这个环即可

合法方案数\((k+1)^{n-1}\times (k-n+1)\)

总方案数\(k^n\)

然后就是高精了

L. 曹冲养猪

crt板子

M. Strange Way to Express Integers

excrt板子

N. 礼物

式子好推就是\(C_n^{sum}\times C_{sum}^{a_1}\times C_{sum-a_1}^{a_2}....\)

然后就是exlucas板子

O. 古代猪文

欧拉定理+lucas+crt

P. Per

可重集的排列+康拓展开+逆元,感觉有点exlucas的思想

posted @ 2022-03-20 22:15  Chen_jr  阅读(163)  评论(0)    收藏  举报