刷题总结——分糖(ssoj 容斥原理+逆元+快速幂+组合数求插板)
题目:
题目描述
有 N 个(相同的)糖果,M 个(不同的)小朋友。M 和 N 满足:1≤M≤N≤100000(105)。
要求:
1.每个小朋友都至少有一个糖果。
2.不存在正整数 X(X>=2),使得每个小朋友的糖果数都是 X 的倍数。
3.糖果不能剩余。
求分糖方法总数。答案模 1000000007(109+7)
输入格式
第一行为数据组数:T<=100000。
接下来 N 行,每行 2 个如上文所示的正整数 N,M。
输出格式
输出 T 行,每行一个整数,为答案。
注意取模!
样例数据 1
备注
【数据范围】
对于 30% 的数据:1<=M<=N<=20
对于 60% 的数据:1<=M<=N<=1000
对于 100% 的数据:1<=M<=N<=100000
题解:
一道题充分证明我在数论上是个sb
首先第一次学到用dfs来求充斥,涨姿势了····
然后就是用组合数来解决将x个糖分到y个小朋友手里的问题····相当于在x-1个空格中插入y-1个板子···转化成组合数···
最后一个问题就是组合数每次肯定只能通过预处理的阶乘相处来求··然而阶乘已经取模···相当于如何计算a/bmodp的问题····
不难想到a/b相当于a*1/b,而1/b%p就是b模p的逆元····且p为质数的情况下逆元直接等于b的p-2次方模p,用快速幂来求即可
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> #include<vector> using namespace std; const int N=1e5+5; const int mod=1e9+7; vector<int>zhiyinzi[N]; long long jc[N],T,n,m,ans,niyuan[N]; bool notprime[N]; inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } inline long long ksm(long long a,long long b) { long long temp=1; while(b) { if(b%2==1) temp=(temp*a)%mod; b/=2; a=(a*a)%mod; } return temp; } inline void pre() { for(int i=2;i<=100000;i++) { if(!notprime[i]) { zhiyinzi[i].push_back(i); for(int j=2;j*i<=100000;j++) { notprime[i*j]=true; zhiyinzi[i*j].push_back(i); } } } jc[0]=1; for(int i=1;i<=100000;i++) jc[i]=(jc[i-1]*i)%mod; for(int i=0;i<=100000;i++) niyuan[i]=ksm(jc[i],mod-2); } inline int calc(int a,int b) { if(a<b) return 0; return ((long long)(jc[a-1]*niyuan[b-1])%mod*niyuan[a-b])%mod; } inline void dfs(int u,int tot,int f) { if(u==zhiyinzi[n].size()) { ans=(ans+f*calc(n/tot,m))%mod; return; } dfs(u+1,tot*zhiyinzi[n][u],-f); dfs(u+1,tot,f); } int main() { //freopen("a.in","r",stdin); pre();T=R(); while(T--) { ans=0;n=R(),m=R(); dfs(0,1,1); cout<<(((ans%mod)+mod)%mod)<<endl; } return 0; }