【BZOJ1005】明明的烦恼(HNOI2008)-Prufer序列+组合计数+高精度

测试地址:明明的烦恼
做法:本题需要用到Prufer序列+组合计数+高精度。
这题就是HNOI2004-树的计数(BZOJ1211)的一个加强版,数据范围增大了,有些点还没有度数限制。但是我们也可以用类似的思路推出答案。我写的BZOJ1211题解看这里
首先,Prufer序列中某些点的出现次数已经确定了,那么这些点之间的排列数量是一个可重排列,设一共有t个点出现次数已经确定,不妨设这些点为1~ts为这些点的出现次数之和,那么这些点之间的排列数量显然为s!/ti=1(di1)!,而这些排列在整个序列中的出现位置有Csn2那么多种,而剩下的n2s个位置可以从t+1~n,共nt个点中任选,所以最后的答案为:
[s!/ti=1(di1)!]×Csn2×(nt)n2s
稍微化一下式子可得:
[(n2)!/[(n2s)!ti=1(di1)!]]×(nt)n2s
然后用和我写的BZOJ1211的题解中类似的处理方法算出最后答案质因数分解的结果,乘起来算出答案即可。由于答案可能很大,所以需要用到高精度计算。最后还要注意判断一下无解,有两种情况是无解的:1.n1而某个di=0;2.s>n2。这样就可以解决此题了。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
int n,d[2010],tot=0,t=0,a[2010][2010],sum[2010][2010],ans[2010],final[3010]={0},digit;
ll p[2010];
bool prime[2010]={0};

void calc_prime(int limit)
{
    for(ll i=2;i<=limit;i++)
    {
        if (!prime[i]) p[++tot]=i;
        for(ll j=1;j<=tot&&i*p[j]<=limit;j++)
        {
            prime[i*p[j]]=1;
            if (i%p[j]==0) break;
        }
    }
}

void mult(int x)
{
    for(int i=1;i<=digit;i++)
        final[i]*=x;
    for(int i=1;i<=digit;i++)
        if (final[i]>9)
        {
            final[i+1]+=final[i]/10;
            final[i]%=10;
            if (i==digit) digit++;
        }
}

int main()
{
    scanf("%d",&n);
    calc_prime(2000);
    for(int i=2;i<=2000;i++)
    {
        for(int j=1;j<=tot;j++)
        {
            sum[i][j]=0;
            int x=i;
            while(x%p[j]==0) sum[i][j]++,x/=p[j];
            a[i][j]=sum[i][j];
            sum[i][j]+=sum[i-1][j];
        }
    }

    int s=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&d[i]);
        if (d[i]!=-1) s+=d[i]-1,t++;
        if (n!=1&&d[i]==0) {printf("0");return 0;}
    }

    if (s>n-2) printf("0");
    else
    {
        for(int i=1;i<=tot;i++) ans[i]=sum[n-2][i];
        for(int i=1;i<=n;i++)
            if (d[i]!=-1)
            {
                for(int j=1;j<=tot;j++)
                    ans[j]-=sum[d[i]-1][j];
            }
        for(int i=1;i<=tot;i++)
            ans[i]-=sum[n-2-s][i];
        for(int i=1;i<=tot;i++)
            ans[i]+=(n-2-s)*a[n-t][i];
        final[1]=1,digit=1;
        for(int i=1;i<=tot;i++)
            for(int j=1;j<=ans[i];j++)
                mult(p[i]);
        bool flag=0;
        for(int i=digit;i>=1;i--)
        {
            if (final[i]) flag=1;
            if (flag) printf("%d",final[i]);
        }
    }

    return 0;
}
posted @ 2017-11-30 20:35  Maxwei_wzj  阅读(121)  评论(0编辑  收藏  举报