• BZOJ 1005[HNOI2008]明明的烦恼


    Description

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

    Input

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

    Output

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

    Solution

    首先我们知道了prufer数列 传送门.

    也知道prufer数列中某个数字出现的次数 = 节点的度数 - 1

    现题目要求某些节点的读书固定, 就是让它在数列中出现的次数固定。

    先不考虑不存在树的情况(特判就懒得写了

    定义$d_i$为节点$i$的度数, $sum = sumlimits_{i=1}^n{d_i-1}$ , $t$ 为被要求的节点的个数

    则数列中固定的数的位置方案有 $C^{sum}_{n-1} imes sum! / prodlimits_{i=1}^n{(d_i-1)!}$种,

    剩下 来$n-t$个节点与剩下来$n-sum-2$个位置, 有$(n-t)^{n-2-sum}$种方案

    两部分不影响, 乘起来就可以得出答案。

    数据有点大, 需要打高精乘法。

    Code

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define rd read()
     5 #define ll long long
     6 using namespace std;
     7 
     8 const int N = 2e3;
     9 const ll base = 1e8;
    10 
    11 int n, sum, c[N], x;
    12 int pri[N], tot, vis[N], cnt[N];
    13 
    14 struct BIG {
    15     ll s[N];
    16     BIG() {
    17         s[0] = 0;
    18         memset(s, 0,sizeof(s));
    19     }
    20     BIG operator * (const ll &t) const{
    21         BIG re;
    22         re.s[0] = s[0];
    23         ll x = 0;
    24         for(int i = 1; i <= s[0]; ++i) {
    25             re.s[i] = s[i] * t;
    26             x = re.s[i] / base;
    27             re.s[i] %= base;
    28         }
    29         if(x) re.s[++re.s[0]] = x;
    30         return re;
    31     }
    32 }ans;
    33 
    34 int read() {
    35     int X = 0, p = 1; char c = getchar();
    36     for(; c > '9' || c < '0'; c  = getchar()) if(c == '-') p = -1;
    37     for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0';
    38     return X * p;
    39 }
    40 
    41 ll fpow(ll a, ll p) {
    42     if(!a) return 1;
    43     ll re = 1;
    44     for(; p; p >>= 1, a = a * a) if(p & 1) re = re * a;
    45     return re;
    46 }
    47 
    48 void init() {
    49     for(int i = 2; i < N; ++i) {
    50         if(!vis[i]) pri[++tot] = i;
    51         for(int j = 1;  j <= tot && pri[j] * i < N; ++j) {
    52             vis[i * pri[j]] = 1;
    53             if(i % pri[j] == 0) break;
    54         }
    55     }
    56 }
    57 
    58 void cal(int x, int k) {
    59     for(int i = 1; x != 1 && i <= tot; ++i) if(x % pri[i] == 0) {
    60         while(x % pri[i] == 0) cnt[i] += k, x /= pri[i];
    61     }
    62 }
    63 
    64 void print(BIG t) {
    65     printf("%lld", t.s[t.s[0]]);
    66     for(int i = t.s[0] - 1; i; --i) printf("%08lld", t.s[i]);
    67     putchar('
    ');
    68 }
    69 
    70 int main()
    71 {
    72     n = rd;
    73     init();
    74     ans.s[0] = 1;
    75     ans.s[1] = 1;
    76     for(int i = 1; i <= n; ++i) {
    77         c[i] = rd;
    78         if(c[i] > 0 && n == 1) return printf("0
    "), 0;
    79         if(c[i] != -1) {
    80             if(c[i] == 0 && n != 1) return printf("0
    "), 0;
    81           c[i]--;    x++;
    82             sum += c[i];
    83             for(int j = 2; j <= c[i]; ++j) cal(j, -1);
    84         }
    85     }
    86     if(n == 1) return printf("1
    "), 0;
    87     if(sum > n - 2) return printf("0
    "), 0;
    88     if(x == n && sum != n - 2) return printf("0
    "), 0;
    89 
    90     for(int i = 1; i <= n - 2; ++i) cal(i, 1);
    91     for(int i = 1; i <= n - sum - 2; ++i) cal(i, -1);
    92 
    93     for(int i = 1; i <= n - sum - 2; ++i) ans = ans * (n - x);
    94     
    95     for(int i = 1; i <= tot; ++i) 
    96         for(int j = 1; j <= cnt[i]; ++j) ans = ans * pri[i];
    97     print(ans);
    98 }
    View Code
  • 相关阅读:
    centos安装libreoffice
    世界,你好!
    4.闭包函数
    3.回调函数
    1内存地址
    2.函数递归
    1.字典
    nonlocal可以修改外层函数变量
    单例模式(Singleton)示例源码
    大家来说说自己的WEB开发工具好吗?
  • 原文地址:https://www.cnblogs.com/cychester/p/9511600.html
Copyright © 2020-2023  润新知