您的位置:首页 > 其它

FZU 1775 Counting Binary Trees(卡特兰数前n项和模m)

2014-11-03 12:13 363 查看
题意:设T(n)为节点不超过n个所能构成不同二叉树的数量。求T(n)%m。

因为我们知道节点数为n个所能构成的二叉树数量是卡特兰数,因为对于n个节点,除了根节点左边放0个右边放n-1个,左边放1个右边放n-2个,以此类推,不会有重复,所以h(n) = h(0)*h(n-1) + h(1)*h(n-2)+....+h(n-2)*h(1)+h(n-1)*h(0)。这就是卡特兰数的递推式。

具体可以看http://baike.baidu.com/view/2499752.htm?fr=aladdin

由于h(n)=C(2n,n)/(n+1) (n=0,1,2,...)故可以推出h(n)=h(n-1)*(4*n-2)/(n+1);

此题要求的是前n项卡特兰数的和,但是那个模m比较麻烦,因为递推式里有个除以n+1,而m不一定是素数,所以不一定有逆元,但是我们可以知道如果没有取模这个是一定除的尽的,所以可以把(4*n-2)/(n+1)这2部分分成与m互素的部分和不互素的部分相乘,互素的部分可以直接用逆元,所以关键就在于不互素的部分,为啥也要把4*n-2也要这样呢?因为需要把n+1约分。

所以整理下思路 ,先把m分解质因数,质因数的个数不会超过12个,因为13!已经超过10^9了,然后用这些质因数去筛4*n-2和n+1,前者互素的部分直接在那个最多12个素数的表上加1,后者就是减1,相当于约分,把这些质因数筛完后,剩下的部分就是与m互素的了,前者直接乘上去,后者直接扩展欧几里德求逆元再乘上去。对了,这题是求前n项卡特兰数的和,所以还要在做完这一切后把表里东西乘上去加给答案。

AC代码:

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<ctime>
#include<string.h>
#include<string>
#include<bitset>
using namespace std;
#define ll long long
#define eps 1e-8
#define NMAX 200005
#define MOD 10007
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
template<class T>
inline void scan_d(T &ret)
{
char c;
int flag = 0;
ret=0;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c == '-')
{
flag = 1;
c = getchar();
}
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
if(flag) ret = -ret;
}
int prime[4000],vis[50005],flag[20],sum[20];
struct node
{
int x,n;
};
node make_node(int x,int n)
{
node t; t.x = x; t.n = n;
return t;
}
int init()
{
int nct = 0;
for(int i = 2; i <= 50000; i++) if(!vis[i])
{
prime[nct++] = i;
for(ll j = (ll)i*(ll)i; j <= 50000; j += (ll)i) vis[j] = 1;
}
return nct;
}

void extern_gcd(int a, int b, int &d, int &x, int &y)
{
if(!b){d = a; x = 1; y = 0;}
else{extern_gcd(b,a%b,d,y,x);y -= x*(a/b);}
}

int calc(ll a,ll b,int m)
{
ll temp=a,solve=1;
while(b)
{
if(b&1)
solve=solve*temp%m;
temp=temp*temp%m;
b>>=1;
}
return solve;
}

int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
// freopen("o4.txt","w",stdout);
#endif // GLQ
int n,m;
int nct = init();
// cout<<nct<<endl;
while(~scanf("%d%d",&n,&m) && n+m)
{
memset(sum,0,sizeof(sum));
ll ans = 1,aa = 1;
int t = m,k = 0;
for(int i = 0; prime[i]*prime[i] <= m; i++) if(t%prime[i] == 0)
{
flag[k++] = prime[i];
while(t%prime[i] == 0) t/=prime[i];
}
if(t > 1) flag[k++] = t;
for(int i = 2; i <= n; i++)
{
t = 4*i-2;
for(int j = 0; j < k; j++) if(t%flag[j] == 0)
{
while(t%flag[j] == 0)
{
sum[j]++;
t /= flag[j];
}
}
ans = (ans*t)%m;
t = i+1;
for(int j = 0; j < k; j++) if(t%flag[j] == 0)
{
while(t%flag[j] == 0)
{
sum[j]--;
t /= flag[j];
}
}
// if(i == 3) cout<<"woshi:"<<t<<endl;
// cout<<t<<endl;
if(t > 1)
{
int d,x,y;
extern_gcd(t,m,d,x,y);
x = (x%m+m)%m;
ans = (ans*x)%m;
}
ll temp = ans;
for(int pp = 0; pp < k; pp++) if(sum[pp])
temp = (temp*calc(flag[pp],sum[pp],m))%m;
aa = (aa+temp)%m;
}
printf("%lld\n",aa);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: