题目:

洛谷2624

分析:

本文中所有的 “树” 都是带标号的。

介绍一种把树变成一个序列的工具:Prufer 序列。

对于一棵 \(n\) 个结点的树,每次选出一个叶子(度数为 \(1\) 的结点),将唯一的那个与它相连的点标号加入 Prufer 序列末尾,然后删去这个叶子及其所连的边,直到最后剩下两个点和一条边。由于每次删且仅删一个点和一条边,所以 Prufer 序列长度为 \(n-2\) 。点 \(a\) 在序列中每次出现都意味着一条与它相连的边被删去了,一直删到 \(a\) 度数为 \(1\) (叶子)为止。所以任意一点在 Prufer 序列中的出现次数就是它的度数减 \(1\) 。

再扯远点。把一个 Prufer 序列还原成树的方式是:定义集合 \(V=\{i|1\leq i \leq n\}\) 每次选出 \(V\) 中编号最小的没有在当前序列出现过的点 \(u\),与 Prufer 序列第一个元素 \(v\) 相连,然后从 \(V\) 中删除 \(u\),并删除 Prufer 序列的第一个元素。最后 \(V\) 中剩余两个元素,将它们相连。

按照上述方法,任意一个由整数 \(1\) 到 \(n\) 组成的序列都可以生成一棵 \(n\) 个结点的树,且任意一棵 \(n\) 个结点的树也可以生成一个这样的序列,所以序列和树一一对应。由此可得推论:\(n\) 个结点的树有 \(n^{n-2}\) 种。

从 Prufer 序列的角度考虑可以解决很多树计数的问题。回到本题。设 \(sum=\sum_{d_i\neq -1} di\) 和 \(num=\sum_{d_i\neq -1} 1\) (即:有 \(num\) 个结点被指定度数,它们的度数之和为 \(sum\) ),则问题被转化为:求一个长为 \(n-2\) 个序列,使其中对于任意 \(i(d_i\neq -1)\) 满足 \(i\) 的出现次数是 \(d_i-1\) 。首先从序列种选出 \(sum-num\) 个位置来放置这些有限制的数,根据可重集合排列公式 \(\frac{(\sum a_i)!}{\prod (a_i!)}\) ,方案数是:

\[C_{n-2}^{sum-num}\frac{(sum-num)!}{\prod_{d_i\neq -1} ((d_i-1)!)}
\]

然后剩下 \(n-2-(sum-num)\) 个位置可以从 \(n-num\) 个数中任取,所以最终答案是:

\[C_{n-2}^{sum-num}\frac{(sum-num)!}{\prod_{d_i\neq -1} ((d_i-1)!)}\cdot (n-num)^{(n-2-(sum-num))}
\]

很不幸发现这题没模数,也看不懂什么分解质因数的做法,乖乖写高精度吧……(其实写这篇博客的主要目的是记录高精度板子qwq

代码:

注意一定要把\(\prod_{d_i\neq -1}((d_i-1)!)\)先全部乘起来再除!!!

算 \(n\) 遍除法会 TLE !!!

(我就不幸因为这个 T 了一发……

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std; namespace zyt
{
typedef long long ll;
const int N = 1e3 + 10;
class long_long_long
{
private:
typedef long_long_long Tril;
const static int DIGIT = 4, BASE = 10000, LEN = (3e3 + 100) / DIGIT;
int data[LEN], len;
public:
long_long_long(const int x = 0)
{
memset(data, 0, sizeof(data));
len = 0;
data[0] = x;
while (data[len])
{
data[len + 1] = data[len] / BASE;
data[len++] %= BASE;
}
}
bool operator == (const Tril &b) const
{
if (len != b.len)
return false;
for (int i = 0; i < len; i++)
if (data[i] != b.data[i])
return false;
return true;
}
bool operator < (const Tril &b) const
{
if (len != b.len)
return len < b.len;
for (int i = len - 1; i >= 0; i--)
if (data[i] != b.data[i])
return data[i] < b.data[i];
return false;
}
Tril operator + (const Tril &b) const
{
Tril ans;
ans.len = max(len, b.len);
for (int i = 0; i < ans.len; i++)
{
ans.data[i] += data[i] + b.data[i];
ans.data[i + 1] += ans.data[i] / BASE;
ans.data[i] %= BASE;
}
if (ans.data[ans.len])
++ans.len;
return ans;
}
Tril operator - (const Tril &b) const
{
Tril ans;
ans.len = len;
for (int i = 0; i < len; i++)
{
ans.data[i] += data[i] - b.data[i];
if (ans.data[i] < 0)
{
--ans.data[i + 1];
ans.data[i] += BASE;
}
}
while (ans.len && !ans.data[ans.len - 1])
--ans.len;
return ans;
}
Tril operator * (const Tril &b) const
{
static ll tmp[LEN];
Tril ans;
if (*this == 0 || b == 0)
return ans;
ans.len = len + b.len - 1;
memset(tmp, 0, sizeof(ll[len + b.len + 2]));
for (int i = 0; i < len; i++)
for (int j = 0; j < b.len; j++)
tmp[i + j] += data[i] * b.data[j];
for (int i = 0; i < ans.len; i++)
{
tmp[i + 1] += tmp[i] / BASE;
ans.data[i] = tmp[i] % BASE;
}
while (tmp[ans.len])
{
tmp[ans.len + 1] += tmp[ans.len] / BASE;
ans.data[ans.len] = tmp[ans.len] % BASE;
++ans.len;
}
return ans;
}
Tril operator / (const Tril &b) const
{
Tril ans, rest;
for (int i = len - 1; i >= 0; i--)
{
rest = rest * BASE + data[i];
if (rest < b)
continue;
int l = 1, r = BASE - 1, anss;
while (l <= r)
{
int mid = (l + r) >> 1;
if (rest < b * mid)
r = mid - 1;
else
l = mid + 1, anss = mid;
}
ans.data[i] = anss;
rest = rest - b * anss;
if (!ans.len)
ans.len = i + 1;
}
return ans;
}
void print()
{
if (!len)
printf("0");
else
{
printf("%d", data[len - 1]);
for (int i = len - 2; i >= 0; i--)
printf("%0*d", DIGIT, data[i]);
}
}
};
typedef long_long_long Tril;
Tril ans, fac[N];
int n;
Tril C(const int n, const int m)
{
return fac[n] / fac[m] / fac[n - m];
}
Tril power(Tril a, int b)
{
Tril ans = 1;
while (b)
{
if (b & 1)
ans = ans * a;
a = a * a;
b >>= 1;
}
return ans;
}
int d[N];
int work()
{
int sum = 0, num = 0;
scanf("%d", &n);
fac[0] = 1;
for (int i = 1; i <= n; i++)
{
fac[i] = fac[i - 1] * i;
scanf("%d", &d[i]);
if (d[i] == -1)
continue;
sum += d[i], ++num;
}
ans = fac[sum - num];
for (int i = 1; i <= n; i++)
if (d[i] > 0)
ans = ans / fac[d[i] - 1];
ans = ans * C(n - 2, sum - num);
ans = ans * power(n - num, n - 2 - (sum - num));
ans.print();
return 0;
}
}
int main()
{
return zyt::work();
}

【洛谷2624_BZOJ1005】[HNOI2008] 明明的烦恼(Prufer序列_高精度_组合数学)的更多相关文章

  1. 【bzoj1005】[HNOI2008]明明的烦恼 Prufer序列+高精度

    题目描述 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? 输入 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i ...

  2. bzoj1005: [HNOI2008]明明的烦恼 prufer序列

    https://www.lydsy.com/JudgeOnline/problem.php?id=1005 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的 ...

  3. bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数

    1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2248  Solved: 898[Submit][Statu ...

  4. BZOJ 1005 [HNOI2008]明明的烦恼 (Prufer编码 + 组合数学 + 高精度)

    1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5786  Solved: 2263[Submit][Stat ...

  5. bzoj 1005 [HNOI2008] 明明的烦恼 (prufer编码)

    [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5907  Solved: 2305[Submit][Status][Di ...

  6. BZOJ 1005: [HNOI2008]明明的烦恼 Purfer序列 大数

    1005: [HNOI2008]明明的烦恼 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/ ...

  7. BZOJ 1005 [HNOI2008]明明的烦恼 purfer序列,排列组合

    1005: [HNOI2008]明明的烦恼 Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少 ...

  8. BZOJ.1005.[HNOI2008]明明的烦恼(Prufer 高精 排列组合)

    题目链接 若点数确定那么ans = (n-2)!/[(d1-1)!(d2-1)!...(dn-1)!] 现在把那些不确定的点一起考虑(假设有m个),它们在Prufer序列中总出现数就是left=n-2 ...

  9. BZOJ 1005 [HNOI2008]明明的烦恼 ★(Prufer数列)

    题意 N个点,有些点有度数限制,问这些点可以构成几棵不同的树. 思路 [Prufer数列] Prufer数列是无根树的一种数列.在组合数学中,Prufer数列是由一个对于顶点标过号的树转化来的数列,点 ...

  10. Luogu P2624 [HNOI2008]明明的烦恼 Prufer+组合+高精

    好的我把标准版过了... 设$ r_i$为$i$的度数 首先,我们设 $ sum = \Sigma r_i-1$,$ tot $ 为所有能够确定度数的点 所以我们有 $ C ^ {sum} _{n-2 ...

随机推荐

  1. 中文情感分析 glove+LSTM

    最近尝试了一下中文的情感分析. 主要使用了Glove和LSTM.语料数据集采用的是中文酒店评价语料 1.首先是训练Glove,获得词向量(这里是用的300d).这一步使用的是jieba分词和中文维基. ...

  2. RabbitMq的简单使用

    本篇将介绍RabbitMq的一个简单使用例子,分别介绍生产者如何发送消息,消费者如何接收和处理消息 关于RabbitMQ的知识背景的文章非常多.我对它的总结是,解决高并发请求的瓶颈,将应用程序真正处理 ...

  3. 最长上升子序列的回溯 ZOJ 2432

    题目大意: 找一组最长上升公共子序列,并把任意一组满足的情况输出出来 最长公共上升子序列不清楚可以先看这篇文章 http://www.cnblogs.com/CSU3901130321/p/41826 ...

  4. 转载 - Struts2 拦截器详细配置过程

    出处:http://www.blogjava.net/zzzlyr/archive/2009/10/12/297998.html Struts2 拦截器详细配置过程 1:所有拦截器的超级接口Inter ...

  5. Spring——ClassPathXmlApplicationContext(配置文件路径解析 1)

    ClassPathXmlApplicationContext     在我的 BeanFactory 容器文章中主要提及了 BeanFactory 容器初始化(Spring 配置文件加载(还没解析)) ...

  6. - > 最大公约数(辗转相除法)和最小公倍数(公式法)

    最大公约数 #include<iostream> using namespace std; int a,b; int gcd(int x,int y){ return x==0?y:gcd ...

  7. js阻碍DOM加载

    今天用谷歌做了个小测试 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  8. Android 自己定义圆圈进度并显示百分比例控件(纯代码实现)

    首先,感谢公司能给我闲暇的时间,来稳固我的技术,让我不断的去探索研究,在此不胜感激. 先不说实现功能,上图看看效果 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZ ...

  9. Oracle执行存储过程报错——ora-01031:权限不足

    执行DDL报错 在oracle存储过程中,默认是可以直接执行DML和DQL的,但是执行CREATE这种的DDL则需要借助EXECUTE IMMEDIATE 如: create or replace p ...

  10. Codeforces Round #273 (Div. 2)D. Red-Green Towers DP

    D. Red-Green Towers   There are r red and g green blocks for construction of the red-green tower. Re ...