排列组合、crt、费马小定理,容斥原理,lucas定理,组合数取模
一些公式&定理
定理
代码
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;
}
错排
考虑加入一个元素,通过一步操作得到错排
-
前个数是错排,随便交换一个,方案数
-
前个数有一个在原位置,共种可能,剩下的数构成错排,只能和它交换,方案数
-
前个数有两个或以上个数在原位置,一步无法得到错排
加起来即可
crt 中国剩余定理
求解
其中两两互质
-
计算,为所有的乘积
-
计算,以及
-
答案
证明,所以得到了需要的答案
严谨过程
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
使用扩展欧几里得算法两两合并
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没啥关系。。
根据唯一分解定定理
求解
就可以使用crt合并
现在考虑计算
无法直接计算,如果移除因子最后统一计算呢
现在考虑求
显然现在就是求这东西
反过来,展开有
后面那两个奇怪的东西就是,只不过分成了两部分
为什么可以这样分?
因为
这东西不用求
递归求解
剩下那个暴力就行
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. 排队
两种情况
- 老师中间是男生
男生排列
老师插空
女生插空
- 老师中间是女生
男生排列
老师+一个女生整体法
剩下女生插空
打个高精
B. combination
模板,关于的证明我没看明白,先不管了
C. 排列计数
基本是错排板子,加一个简单组合数就行了
D. Perm 排列计数
把大小关系画出来,发现是个二叉堆,然后组合数+树DP
E. 集合计数
容斥
表示交集至少个的方案数
首先钦定这个交集
剩下的个数能构成个集合,每个集合都可以选或不选,去掉空集
然后容斥一下就行了
F. 牡牛和牝牛
简单DP,有排列组合做法,但没必要
G BZOJ 4403序列统计
首先是假的,只有有用
然后枚举长度,隔板法处理
H DZY Loves Math II
题意转化个不同元素,取个元素,有多少种取法,直接组合数就行
x=
然后问题就是一个背包了...吗?
发现值域太大,完全跑不出来
但是可以发现
注意不一定小于
分成两部分求解
,每个可以由个构成,每个都有可能,用隔板法就是
这部分直接背包就行,但是注意由于上面的计算,这里需要保证每个使用次数小于
多重背包?其实不用,完全背包再加一个就行了
I. 虔诚的墓主人
这题我觉得是扫描线,但是严重怀疑不是正解,然后,,,就是扫描线,加上一个简单的组合数就行
J. 地精部落
妙极了,不会做的蓝题系列
首先看性质
- 在一个数列中,若与不相邻,直接交换这两个数字就可以组成一个新的数列
- 把波动数列中的每个数字变成会得到互补的数列,即且新数列的山峰与山谷情况相反
- 数列有对称性
状态设计
表示选择这些数字,第一位是并且是山峰的方案数
转移
因为性质有(中,因为是峰,一定不与相邻)
如果与相邻,那么把放到以为开头并且是谷的数列前,因为性质2,那么有
答案
因为性质3,答案就是
K. 看电影
这题应该放到高精题单里!
假设存在一个号座位,然后把座位与座位连上,这样每个人都有座位,合法的方案里第个座位应该是没有人的,枚举哪个位置是断开这个环即可
合法方案数
总方案数
然后就是高精了
L. 曹冲养猪
crt板子
M. Strange Way to Express Integers
excrt板子
N. 礼物
式子好推就是
然后就是exlucas板子
O. 古代猪文
欧拉定理+lucas+crt
P. Per
可重集的排列+康拓展开+逆元,感觉有点exlucas的思想
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】