zoukankan      html  css  js  c++  java
  • BZOJ 1005: [HNOI2008]明明的烦恼

    传送门

    prufer序列

    因为每个prufer序列唯一对应一颗树,所以如果能求出prufer序列的方案数就能求出树的方案数

    一颗 n 个节点的树的prufer序列有 n-2 个数字,每个数字在 [1,n] 的范围内,表示节点编号

    对于一个度数为 $d_i$ 的点,它会在prufer序列中出现 $d_i-1$ 次

    设 m 为度数确定的节点数量,$d_i$ 为某个节点的度数

    设 $sum=sum _{i=1}^{m}(d_i-1)$

    那么已经确定的节点在 prufer 序列中占据了 sum 个位置

    这 sum 的位置在 n-2 个位置中放置的方案数显然为 $C^{sum}_{n-2}$

    然后单独考虑这个长度为 sum 的序列的情况

    对于确定度数的节点 1,它要在长度为 sum 的序列中选 $d_1-1$ 个位置

    有 $C^{d_1-1}_{sum}$ 种方案

    对于第 2 个节点,因为节点 1 先选好了,所以它要在长度为 $sum-(d_1-1)$ 的序列中选 $d_2-1$ 个位置

    有 $C^{d_2-1}_{sum-(d_1-1)}$ 种方案

    这样推广下去,用乘法原理把所有的 C 乘起来(化简过程自己推一下)

    最后得到 $C^{sum}_{n-2}frac{sum!}{prod ^{m}_{i=1}(d_i-1)}$

    然后考虑不确定的点,剩下 n-2-sum 个位置顺便填剩下 n-m 个数

    有 $(n-m)^{(n-2-sum)}$ 种方案

    最后一起乘起来就好了

    答案要高精度,但是因为最后答案一定是整数,所以可以提出分子分母的质因数化简分式,不用高精除

    高精怕麻烦没写压位

    别忘了判断无解

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=20007;
    int n,d[N],m,sum;
    int pri[N],tot,f[N];//f[i]表示i最小的质因数
    bool not_pri[N];
    void pre()//欧拉筛求每个数最小的质因数
    {
        not_pri[1]=1; f[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!not_pri[i]) { pri[++tot]=i; f[i]=i; }//质数的最小质因数就是本身
            for(int j=1;j<=tot;j++)
            {
                int g=i*pri[j]; if(g>n) break;
                not_pri[g]=1; f[g]=pri[j];
                if(i%pri[j]==0) break;
            }
        }
    }
    int cnt[N];//cnt存每个质因数出现的次数
    inline void work(int x,bool p)//p表示此时化简的是分子还是分母
    {
        if(p) while(x!=1) cnt[f[x]]++,x/=f[x];
        else while(x!=1) cnt[f[x]]--,x/=f[x];
    }
    struct BIGINT//高精度存答案
    {
        int a[N],len;
        BIGINT() { memset(a,0,sizeof(a)); len=1; }
        inline void mul(int tmp)//高精乘低精
        {
            for(int i=1;i<=len;i++) a[i]*=tmp;
            for(int i=1;i<=len;i++)
            {
                a[i+1]+=a[i]/10;
                a[i]%=10;
            }
            while(a[len+1]) len++,a[len+1]+=a[len]/10,a[len]%=10;
        }
        inline void print() { for(int i=len;i;i--) printf("%d",a[i]); }//输出
    };
    int main()
    {
        int a;
        n=read(); pre();
        for(int i=1;i<=n;i++)
        {
            a=read(); if(a==-1) continue;
            if(!a) { printf("0"); return 0; }//判断无解
            d[++m]=a; sum+=a-1;
        }
        if(sum>2*n-2) { printf("0"); return 0; }//判断无解
        for(int i=n-1-sum;i<=n-2;i++) work(i,1);
        for(int i=1;i<=m;i++)
            for(int j=2;j<=d[i]-1;j++) work(j,0);
        for(int i=1;i<=n-2-sum;i++) work(n-m,1);
        BIGINT ans; ans.a[1]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<=cnt[i];j++)
                ans.mul(i);
        ans.print();
        return 0;
    }
  • 相关阅读:
    从jdbc到分层的飞跃
    第二章 变量和数据类型
    s1300新学期伊始的我们
    选择结构总结
    第四章 选择结构(二) Switch结构
    第三章 选择结构(一)
    第二章 变量、数据类型和运算符
    使用Java理解程序逻辑 第1章 初识Java 基本部分
    ES命令基础
    Spring MVC拦截器
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10104542.html
Copyright © 2011-2022 走看看