1. 组合数取模
求 (dbinom nmmod p)
1. (n,mle 200),(p) 任意
递推
2. (n,mle 10^6),(pge 10^9) 素数
预处理 (n!),(m!^{-1}),((n-m)!^{-1}) 即可 .
3. (n,mle 10^6),(ple 2000) 素数
注意到 (n) 可能是 (p) 的倍数,故逆元可能不存在 .
引入 Lucas 定理:
设 (n,m) 在 (p)((p) 是质数)进制下表示为
[overline{n_kn_{k-1}n_{k-2}dots n_1}\,,overline{m_km_{k-1}m_{k-2}dots m_1} ]其中 (0le n_i<p),(0le m_i<p) .
则有:
[dbinom{n}{m}equiv dbinom{n_k}{m_k}dbinom{n_{k-1}}{m_{k-1}}cdotsdbinom{n_1}{m_1}pmod p ]
Proof:
原问题等价于 (C^n_mequiv C^{nmod p}_{mmod p}cdot C^{lfloor n/p
floor}_{lfloor m/p
floor}pmod p) .
设 (xinmathbb N^+),且 (x<p),则:
由于 (x<p) 且 (p) 是素数,所以 (x) 存在模 (p) 意义下的逆元,因此:
因为 (pcdot x^{-1}cdot C^{x-1}_{p-1}equiv 0pmod p) ,故 (C^x_pequiv0pmod p).
由二项式定理,
除了 (i=0,i=p) 的项数,别的模 (p) 均为 (0),所以:
现在我们设:
从而:
再由二项式定理有
而同时又有:
注意到 (ip+j) 正好能取到 (0) 到 (m) 内所有整数一次,所以枚举 (k=ip+j),得
结合二项式定理,得
对比系数,得:
命题获证 .
4. (n,mle 10^6),(p=p_1p_2cdots p_k),(p_1,p_2,cdots,p_kle 2000) 为互不相同的素数
令
则
用 Lucas 定理求组合数,然后 CRT 合并即可
2. 抽屉原理,容斥原理
1. 抽屉原理
抽屉原理:
(n+1) 个物品放入 (n) 个抽屉,必有一抽屉至少有两个物品 .
2. 容斥原理
[left|igcup_{i=1}^n A_i ight|=sum_{Bsubseteq{A_1,A_2,cdots,A_n}}(-1)^{|B|+1}left|igcap_{A_iin B}A_i ight| ]
典型题:(n) 夫妻问题
(n) 对夫妻排成一圈,共 (2n) 人,夫妻不能相邻,求方案数 .
前置:圆排列
(n) 个人排成一圈,方案数为 ((n-1)!)(旋转算一种)(所以去除 (n))
对 (n) 夫妻问题固定每个夫妻,用容斥原理:
小题
1. 组合数问题
请你把 (n) 拆成 (k) 个不同的组合数之和,输出任意一种方案
两个组合数 (dbinom{n_1}{m_1}\,,\,dbinom{n_2}{m_2}) 不同当且仅当 (n_1 eq n_2) 或 (m_1 eq m_2)
(nle 10^9),(kle 10^3) .
乱构造
2. 组合数问题
(k) 个不同(不同的定义同上题)的组合数,求它们和的最大值 .
对于选取的组合数 (dbinom pq) 要求 (0le p,qle n)
(nle 10^6),(kle 10^5) .
用一个堆维护最值,每次加入杨辉三角形中四周的组合数即可 .
比较两个组合数取对数即可 .
3. 组合数问题
求
[sum_{i=0}^{infty}mathrm{C}_{nk}^{ik+r} ]
注意到对组合数作 (k) 次展开可以得到一个引理
Lemma
[dbinom nm=sum_{i=0}^kdbinom{n-k}{m-i}dbinom ki ](k) 为任意整数
令
然后矩阵快速幂优化,时间复杂度 (O(k^3log n)) .
4. 万圣节
给定 (n) 个数,找若干个数使得它们是 (c) 的倍数 .
前缀和
5. 正方形
给定平面上 (n) 点,用三个 (L imes L) 正方形覆盖所有点,求最小的 (L) .
二分 (L),判定就是两个正方形都放在角上,剩下一个再判定
下午比赛
380 pts 混了个 rank1 .
T1
给定 (x,y),求 (xmod y) .
(xle 10^{100000}\,,yle 2^{31})
有手就行
T2
大小为 (3 imes N) 的方格图,你需要用有(M)种颜色的大小为 (1 imes 1) 和 (2 imes 2) 的小方块去覆盖整个方格图。小方块和小方块之间不能互相覆盖,并且必须将整个方格图全部覆盖。求有多少种覆盖方格图的方法。两种方案不一样当且仅当存在某个位置颜色不一样或者摆放小方块的方法不一样。
.(N,Mle 10^9)
推(?)个式子,然后矩乘优化
T3
求
[x^{C(n,m)} mod 1000003471 ](1le x,n,mle 10^5) .
欧拉降幂,对模数质因数分解,Lucas 定理求然后 CRT 合并
T4
给定 (N) 个数,其中第 (i) 个数为 (gcd(x,y+i-1)) . 求任意一组可能的 (x,y) 的值 .
(1leq Nleq1000),保证存在答案 (x,y≤10^9) .
考场上胡了个 (O(y)) 暴力,80pts .
显然 (x=operatorname{lcm}(a_1,a_2,cdots,a_N)) .
用 (a_i=gcd(x,y+i-1)) 列出同余方程,CRT 解出 (y) 即可 .
Code
T1
using namespace std;
typedef long long ll;
string str;
ll p,ans;
int main()
{
cin>>str; int l=str.length();
scanf("%lld",&p);
for (int i=0;i<l;i++) ans=(ans*10+str[i]-'0')%p;
printf("%lld
",ans);
return 0;
}
T2
using namespace std;
typedef long long ll;
const int MOD=1e9+7,N=5;
ll n,m;
ll qpow(ll x,ll n,ll p)
{
ll ans=1;
while (n)
{
if (n&1) ans=ans*x%p;
n>>=1; x=x*x%p;
} return ans;
}
struct mat
{
ll a[N][N];
mat operator *(const mat& mD)
{
mat ans; memset(ans.a,0,sizeof ans.a);
for (int i=1;i<=2;i++)
for (int k=1;k<=2;k++)
for (int j=1;j<=2;j++)
ans.a[i][j]=(ans.a[i][j]+mD.a[k][j]*a[i][k]%MOD)%MOD;
return ans;
}
mat operator ^(int q)
{
mat ans,b=*this;
ans.a[1][1]=(qpow(m,6,MOD)+2*qpow(m,3,MOD))%MOD; ans.a[1][2]=qpow(m,3,MOD);
while (q)
{
if (q&1) ans=ans*b;
b=b*b; q>>=1;
} return ans;
}
}B;
int main()
{
scanf("%lld%lld",&n,&m);
if (n==1){printf("%lld
",qpow(m,3,MOD)); return 0;}
if (n==2){printf("%lld
",(qpow(m,6,MOD)+2*qpow(m,3,MOD))%MOD); return 0;}
B.a[1][1]=qpow(m,3,MOD); B.a[1][2]=1;
B.a[2][1]=2*qpow(m,3,MOD); B.a[2][2]=0;
printf("%lld",(B^(n-2)).a[1][1]%MOD);
return 0;
}
T3
using namespace std;
typedef long long ll;
const int List[]={2,3,5,53,677,929},MOD=1000003471;
const int N=1e4+5;
ll C[N][N];
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
int Lucas(int n,int m,int p)
{
for (int i=0;i<p;i++)
{
C[i][0]=1;
for (int j=1;j<=i;j++)
{
C[i][j]=C[i-1][j-1]+C[i-1][j];
if (C[i][j]>=p) C[i][j]-=p;
}
} // init
int ans=1;
while (n||m){ans=ans*C[n%p][m%p]; n/=p; m/=p;}
return ans;
}
ll qpow(ll x,ll n,ll p)
{
ll ans=1;
while (n)
{
if (n&1) ans=ans*x%p;
n>>=1; x=x*x%p;
} return ans;
}
int x,n,m;
ll M=1000003470,ans=0;
int main()
{
scanf("%d%d%d",&x,&n,&m);
for (int i=0;i<6;i++)
{
ll Q=List[i],P=Lucas(n,m,Q);
ans+=P*(M/Q)%M*qpow(M/Q,Q-2,Q)%M;
} printf("%lld
",qpow(x,ans,MOD)%MOD);
return 0;
}
T4 80pts(应该没人想看这里)
using namespace std;
typedef long long ll;
const int MOD=1e9+7,N=2050;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll n,a[N],x=1;
int main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++) scanf("%lld",a+i),x=lcm(x,a[i]);
for (ll D=a[1];D<=1e9;D+=a[1])
{
bool vis=true;
if (D+n-1>1e9) continue;
for (ll now=D+1;now<=D+n-1;now++)
if (now%a[now-D+1]){vis=false; break;}
if (vis){printf("%lld %lld
",x,D); return 0;}
} printf("stO zhx Orz");
return 0;
}
// 4 3 2
T4
using namespace std;
typedef long long ll;
const int N=2050;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll n,a[N],p,q,x=1;
void merge(ll& a1,ll& m1,ll a2,ll m2)
{
if (m2>m1) swap(m1,m2),swap(a1,a2);
while (a1%m2!=a2) a1+=m1;
m1=lcm(m1,m2);
}
int main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++) scanf("%lld",a+i),x=lcm(x,a[i]);
p=0; q=1;
for (int i=1;i<=n;i++)
{
int P=((1-i)%a[i]+a[i])%a[i],Q=a[i];
merge(p,q,P,Q);
} printf("%lld %lld",x,p);
return 0;
}