zoukankan      html  css  js  c++  java
  • [HNOI2008][bzoj 1005]明明的烦恼(prufer序列)

    1005: [HNOI2008]明明的烦恼

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 7121  Solved: 2816
    [Submit][Status][Discuss]

    Description

      自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
    任意两点间连线,可产生多少棵度数满足要求的树?

    Input

      第一行为N(0 < N < = 1000),
    接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1

    Output

      一个整数,表示不同的满足要求的树的个数,无解输出0

    Sample Input

    3
    1
    -1
    -1

    Sample Output

    2

    HINT

      两棵树分别为1-2-3;1-3-2

    题解:

    树的计数题目,可以想到是用prufer序列来求解。

    先来科普一下prufer的性质:

    1. 每个prufer序列都唯一对应着一棵树。
    2. prufer序列的长度等于它所对应的树的节点数-2。
    3. 每个数在prufer序列中出现的次数等于该节点在树中的度数-1。

    其实有了这些性质我们就可以做题了(想要证明的自行度娘),现在我们在来观察一下这道题,如果他给出的是所有点的度数,那么这道题就是一个不全相异的全排列个数(戳这里),但是他给出的点的度数只是一部分的,那我们就可以先当别的点不存在,先把这一部分的方案数求出来,设$tot=Sigma{d[i]-1}$,$tot$即为已经确定度数的点在prufer序列里所占的个数,这一部分方案数为$C_{n-2}^{tot}$,但是别忘了我们还要处理重复的部分,处理第一个数向$tot$个数中插的方案数为$C_{tot}^{d[1]-1}$,同理处理第二个数的方案数是$C_{tot-(d[1]-1)}^{d[2]-1}$,剩下的以此类推。

    但是别忘了我们还有没确定度数的点,但是这很好处理,我们设未确定的点数为$cnt$,这就相当于在$n-2-tot$的空间中随便选$cnt$个,那么答案即为$cnt^{n-2-tot}$

    然后我们根据乘法原理可以的出答案

    $ans=C_{n-2}^{tot}*C_{tot}^{d[1]-1}*C_{tot-(d[1]-1)}^{d[2]-1}*cdots*C_{d[i]-1}^{d[i]-1}*cnt^{n-2-tot}$

    我们把组合数公式展开来一波化简就得到了结果(数学公式崩了,凑或者看吧qwq)

    这样再用一个高精就阔以了。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<queue>
     6 #include<vector>
     7 using namespace std;
     8 #define int long long
     9 const int N=500005;
    10 int d[N];
    11 struct BigInt{
    12     int m[N];
    13     friend void operator *= (BigInt &a,int b){
    14         int x=0;
    15         for(int i=1;i<=a.m[0];i++){
    16             int y=a.m[i]*b+x;
    17             a.m[i]=y%10;
    18             x=y/10;
    19         }
    20         while(x){
    21             a.m[++a.m[0]]=x%10;
    22             x/=10;
    23         }
    24     }
    25     friend void operator /= (BigInt &a,int b){
    26         int x=0;
    27         for(int i=a.m[0];i>=1;i--){
    28             x+=a.m[i];
    29             a.m[i]=x/b;
    30             x%=b;
    31             x*=10;
    32         }
    33         while(a.m[a.m[0]]==0&&a.m[0]>1) a.m[0]--;
    34     }
    35     friend void print(BigInt a){
    36         for(int i=a.m[0];i>=1;i--) printf("%lld",a.m[i]);
    37         puts("");
    38     }
    39 }x;
    40 signed main(){
    41     int n;
    42     scanf("%lld",&n);
    43     x.m[0]=x.m[1]=1;
    44     int cnt=0,num=0;
    45     if(n==1){
    46         int k;
    47         scanf("%lld",&k);
    48         if(!k){puts("0");}
    49         else puts("1");
    50         return 0;
    51     }
    52     for(int i=1;i<=n;i++){
    53         int mm;
    54         scanf("%lld",&mm);
    55         if(!mm){puts("0");return 0;}
    56         if(mm==-1) cnt++;
    57         else {d[i]=mm-1;num+=d[i];}
    58     }
    59     for(int i=1;i<=n-2;i++) x*=i;
    60     //for(int i=1;i<=num;i++) x*=i;
    61     //for(int i=2;i<=/*n-cnt-2*/num;i++) x*=i;
    62     for(int i=1;i<=n-2-num;i++) x*=cnt,x/=i;
    63     for(int i=1;i<=n;i++){
    64         if(d[i]>0){
    65             for(int j=1;j<=d[i];j++) x/=j; 
    66         }
    67     }
    68     print(x);
    69 }
    View Code
  • 相关阅读:
    C++学习:explicit关键字
    CMake入门——简单CMakeLists的编写
    EOJ Monthly 2020.1 E. 数的变幻
    CF #610Div2 B2.K for the Price of One (Hard Version) (dp解法 && 贪心解法)
    hdu 5147 Sequence II【树状数组/线段树】
    hdu 6581 Vacation【思维】
    dubbo源码学习(一)之ExtensionLoader
    xsd学习教程
    spring自定义schema学习
    如何更好地学习dubbo源代码(转)
  • 原文地址:https://www.cnblogs.com/leom10/p/11235578.html
Copyright © 2011-2022 走看看