1005: [HNOI2008]明明的烦恼

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3032  Solved: 1209

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

Source

大意:给出n个点的度数限制,问这些点能组成多少棵树

分析:

首先介绍一种数列Purfer Code:

这种数列是这样子的:对于一棵树(这棵树是不存在根的),每次在数列中加入编号最小的孩子节点(即为度数为1的节点)所对应的父亲节点,并在树中删除此点,不断重复此操作,知道这棵树只剩下两个点

如:

1

/     \

2         3

/     \

4        5

所对应的PurferCode就是:1 2 2

因为第一次删掉3,第二次删掉1,第三次删掉4

剩下2、5两个节点

可以发现,每个PurferCode对应的树是唯一的

我们也可以从PurferCode得到一棵树

首先由PurferCode计算出每点的度数(出现的次数+1),

然后每次(第 i 次)选择度数最少(度数大于1)的点(度数相同时编号较小的优先)与数列第 i 个点连边,然后这两个点的度数都-1,

这样不断操作,会最后的到两个度数为1的点,将这两点相连,即的原来的树

设度数有限制的点的个数为Cnt,度数-1之和为Sum(即为数列中这些点要占的个数),第 i 个点的度数为di

那么可以根据组合数学:

从数列n-2个点中选择Sum个点有C(Sum, n-2)

这Sum个点中组合C(d1-1, Sum)*C(d2-1, Sum-(d1-1))*C(d3-1, Sum-(d1-1)-(d2-1))*........*C(dn - 1, dn - 1)

剩下的n-2-Sum个位置由其余n-Cnt个点组合,有(n-Cnt)^(n-2-Sum)种方案

所以Ans = C(Sum, n-2)*C(d1-1, Sum)*C(d2-1, Sum-(d1-1))*C(d3-1, Sum-(d1-1)-(d2-1))*........*C(dn - 1, dn - 1)*(n-Cnt)^(n-2-Sum)

因为C(Sum, n-2) = (n-2)!/(Sum!*(n-2-Sum)!),

C(d1-1, Sum) = Sum!/((d1-1)!*(Sum-(d1-1))!

C(d2-1, Sum-(d1-1)) = (Sum-(d1-1))!/((d2-1)!*(Sum-(d1-1)-(d2-1))!)

.........

相乘时加粗部分可以相消

最终可得:

Ans =  [ (n-2)!*((n-Cnt)^(n-2-Sum)) ]  /    [(n-2-Sum)!*(d1-1)!*(d2-1)!*.....*(dn-1)!]

写程序时,乘除都会很大,高精度除法和高精度乘高精度都很麻烦,所以可以分解质因数,最终可以只写单精度乘高精度即可。

关于分解x!的质因数

x = p^a   *    k(即x可以整除p^a)

a = x div p  +  x div p^2 + x div p^3 + ..... + x div p^m (p^m为p的阶乘中小于x的最大阶乘,即:  p^m <= x,  p^(m+1) > x)

那么 x!= p1^x1 * p2^x2  *  p3^x3 * ...... * pn^xn

这个是显然的,大家写写就知道

关于分解质因数,可以用O(n)筛素数,这个不讲

综上所述,本题得解

 #include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <ctime>
using namespace std;
typedef long long LL;
#define For(i, s, t) for(int i = (s); i <= (t); i++)
#define Ford(i, s, t) for(int i = (s); i >= (t); i--)
#define MIT (2147483647)
#define INF (1000000001)
#define MLL (1000000000000000001LL)
#define sz(x) ((bnt) (x).size())
#define clr(x, y) memset(x, y, sizeof(x))
#define puf push_front
#define pub push_back
#define pof pop_front
#define pob pop_back
#define ft first
#define sd second
#define mk make_pair
inline void SetIO(string Name) {
string Input = Name+".in",
Output = Name+".out";
freopen(Input.c_str(), "r", stdin),
freopen(Output.c_str(), "w", stdout);
} const int N = , LEN = , M = ;
int Prime[N], p;
bool Visit[N];
int n, D[N], Cnt, Sum;
int P1[N], P2[N];
int Ans[LEN], Len; inline void Input() {
scanf("%d", &n);
For(i, , n) scanf("%d", &D[i]);
} inline void EXIT(int Ans) {
printf("%d\n", Ans);
exit();
} inline void BuildPrime() {
clr(Visit, ), p = ;
For(i, , n){
if(!Visit[i]) Prime[++p] = i;
For(j, , p) {
if(i*Prime[j] > n) break;
Visit[i*Prime[j]] = ;
if(i%Prime[j] == ) break;
}
}
} inline void FenJie(int A, int *P) {
For(i, , p) {
int x = Prime[i];
while(x <= A) {
P[i] += A/x;
x *= Prime[i];
}
}
} inline void Mul(int x) {
For(i, , Len) Ans[i] *= x;
For(i, , Len) {
Ans[i+] += Ans[i]/M;
Ans[i] %= M;
}
while(Ans[Len+]) {
Len++;
Ans[Len+] += Ans[Len]/M;
Ans[Len] %= M;
}
} inline void Solve() {
//Check
if(n == ) {
if(D[] == || D[] == -) EXIT();
EXIT();
}
For(i, , n)
if(D[i] == ) EXIT();
Cnt = Sum = ;
For(i, , n)
if(D[i] > ) Cnt++, Sum += D[i]-;
if(Sum > n- || (Sum < n- && n == Cnt)) EXIT(); //Work
BuildPrime();
clr(P1, ), clr(P2, );
FenJie(n-, P1);
FenJie(n--Sum, P2);
For(i, , n)
if(D[i] > ) FenJie(D[i]-, P2);
int m = n-Cnt;
For(i, , p) {
if(m <= ) break;
int t = , k = Prime[i];
while(!(m%k)) m /= k, t++;
P1[i] += t*(n--Sum);
}
For(i, , p) P1[i] -= P2[i]; Ans[] = , Len = ;
For(i, , p)
For(j, , P1[i]) Mul(Prime[i]); printf("%d", Ans[Len]);
Ford(i, Len-, ) printf("%04d", Ans[i]);
cout<<endl;
} int main() {
SetIO("");
Input();
Solve();
return ;
}

下面聊聊用线性筛素数法求欧拉函数

欧拉函数有两个定理

1、当m,n互质时,phi(m*n) = phi(m)*phi(n);

2、phi(p^k) = (p-1)*p^(k-1)

因为1-p^k间只有p的倍数不与其互质,这样的数有p^k/p个,即p^(k-1)个,即phi(p^k) = p^k - p^(k-1) = (p-1)*p^(k-1)

那么当n%p == 0 (显然不互质,p为质数)时,

设n = m * p^k(m与p互质)

phi(n) = phi(m)*phi(p^k) = phi(m)*(p-1)*p^(k-1)

phi(n*p) = phi(m*p^(k+1)) = phi(m)*(p-1)*p^k = phi(n)*p

所以可以得求欧拉函数代码

 for(int i=;i<N;i++)  {
if(!vis[i]) {
prime[++cnt]=i;
phi[i]=i-;
}
for(int k=;k<=cnt&&prime[k]*i<N;k++) {
x=prime[k]*i;
vis[x]=true;
if(i%prime[k]==) {
phi[x]=phi[i]*prime[k];
break;
}
else phi[x]=phi[i]*(prime[k]-);
}
}

bzoj1005 [HNOI2008]明明的烦恼的更多相关文章

  1. bzoj1005: [HNOI2008]明明的烦恼(prufer+高精度)

    1005: [HNOI2008]明明的烦恼 题目:传送门 题解: 毒瘤题啊天~ 其实思考的过程还是比较简单的... 首先当然还是要了解好prufer序列的基本性质啦 那么和1211大体一致,主要还是利 ...

  2. [BZOJ1005] [HNOI2008] 明明的烦恼 (prufer编码)

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

  3. 【prufer编码+组合数学】BZOJ1005 [HNOI2008]明明的烦恼

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

  4. BZOJ1005 HNOI2008明明的烦恼(prufer+高精度)

    每个点的度数=prufer序列中的出现次数+1,所以即每次选一些位置放上某个点,答案即一堆组合数相乘.记一下每个因子的贡献分解一下质因数高精度乘起来即可. #include<iostream&g ...

  5. BZOJ1005:[HNOI2008]明明的烦恼(组合数学,Prufer)

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

  6. [bzoj1005][HNOI2008][明明的烦恼] (高精度+prufer定理)

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

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

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

  8. [bzoj1005][HNOI2008]明明的烦恼-Prufer编码+高精度

    Brief Description 给出标号为1到N的点,以及某些点最终的度数,允许在 任意两点间连线,可产生多少棵度数满足要求的树? Algorithm Design 结论题. 首先可以参考这篇文章 ...

  9. [BZOJ1005][HNOI2008]明明的烦恼 数学+prufer序列+高精度

    #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int N; ...

随机推荐

  1. 从键盘输入成绩,找出最高分,并输出学生成绩等级。成绩>=最高分-10,为A,成绩>=最高分-20,为B,成绩>=最高分-30,为C,其余等级为D

    import java.util.Scanner;public class TestStudent { public static void main(String[] args) { //从键盘获得 ...

  2. highcharts的简单使用

    在使用过的图表js插件中,个人认为还是highcharts最好,无论从兼容性,渲染速度,甚至是文档详细上来说,都一直觉得highcharts更胜一筹.现在花点时间做一下简单的总结,比如从一个矩形图开始 ...

  3. Android浏览本地 API文档 + 解决页面加载慢的问题

    火狐浏览器安装离线浏览插件: 用浏览器打开index.html文件,你会发现加载的很慢,原因你懂的,为此,我们可以通过离线的方式 查看本地API文档,用火狐浏览器  +   Work Offline插 ...

  4. 排队打饭 sdut 2443【最简单的贪心法应用举例】

    排队打饭 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 题目链接:http://acm.sdut.edu.cn/sdutoj/p ...

  5. 【数据结构】建立和平衡AVL二叉树

    一步一步写平衡二叉树(AVL树) 原文地址:http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html 我添加了一些内容,以充实整个算 ...

  6. [LeetCode] Add Two Numbers

    You are given two linked lists representing two non-negative numbers. The digits are stored in rever ...

  7. maven pom.xml 说明

    本文复制于:http://blog.csdn.net/zhuxinhua/article/details/5788546 一.pom.xml示例 <project> <modelVe ...

  8. 无法打开包括文件:“windows.h”: No such file or directory

      VS2012 出现如下错误: 无法打开包括文件:"windows.h": No such file or directory   解决办法,将 C:\Program Files ...

  9. win7-32 系统 + VS2010 配置 glew

    网上下载的程序,运行时报错:  C1083: 无法打开包括文件:“gl\glew.h”: No such file or directory. 百度一下,发现需要配置 glew 库. 方法如下: 下载 ...

  10. C++ 中 char 与 int 转换问题

    itoa 功  能:把一整数转换为字符串 函  数:char *itoa(int value, char *string, int radix); 解  释:itoa 是英文integer to ar ...