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

来源:互联网 发布:php官方手册 编辑:程序博客网 时间:2024/06/05 00:30

题意:设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|1template<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;}


0 0
原创粉丝点击