Counting Binary Trees
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 564 Accepted Submission(s): 184
Problem DescriptionThere are 5 distinct binary trees of 3 nodes:
Let T(n) be the number of distinct non-empty binary trees of no more than n nodes, your task is to calculate T(n) mod m.InputThe input contains at most 10 test cases. Each case contains two integers n and m (1 <= n <= 100,000, 1 <= m <= 109) on a single line. The input ends with n = m = 0.OutputFor each test case, print T(n) mod m.Sample Input3 100 4 10 0 0Sample Output8 2Source
题意:求Catalan数的前n项和。
直接递推公式就好了,但是有一个问题,递推式里有除法,而由于除数与模数不互质,不能预处理逆元,这里有一个求不互质同余除法的方法(前提是结果必须是整数,所以只能用来求Catalan,Stirling和组合数这样的数)
$frac{a}{b}equiv c (mod d)$,我们先将d质因数分解,然后对于$a$和$b$将d的质因子部分单独统计,剩余部分直接exgcd求逆元即可。
因为剩余部分满足互质所以可以直接做逆元,而我们有$pequiv p(mod ap)$,所以最后质因子部分直接乘就可以了。
这样就解决了HNOI2017的70分做法。
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> #define rep(i,l,r) for (int i=l; i<=r; i++) using namespace std; const int maxn=100010; typedef long long ll; ll ans,cnt[maxn]; vector<int> prime; int n,m; void exgcd(ll a,ll b,ll &x,ll &y){ if (!b) x=1,y=0; else exgcd(b,a%b,y,x),y-=x*(a/b); } ll inv(ll a,ll p){ ll x,y; exgcd(a,p,x,y); return (x+p)%p; } void getPrime(){ ll t=m; for (int i=2; i*i<=t; i++) if (t%i==0){ prime.push_back(i); while (t%i==0) t/=i; } if (t>1) prime.push_back(t); } void solve(){ getPrime(); ans=1; ll res=1; rep(i,2,n){ ll fz=4*i-2,fm=i+1; for (int k=0; k<(int)prime.size(); k++) if (fz%prime[k]==0) while (fz%prime[k]==0) fz/=prime[k],cnt[k]++; res=(res*fz)%m; for (int k=0; k<(int)prime.size(); k++){ if (fm%prime[k]==0) while (fm%prime[k]==0) fm/=prime[k],cnt[k]--; } if (fm>1) res=(res*inv(fm,m))%m; ll t=res; for (int k=0; k<(int)prime.size(); k++) rep(s,1,cnt[k]) t=(t*prime[k])%m; ans=(ans+t)%m; } printf("%lld ",ans); } int main(){ while(~scanf("%d%d",&n,&m) && n+m) prime.clear(),memset(cnt,0,sizeof(cnt)),solve(); return 0; }