BZOJ1005明明的烦恼 Prufer + 分解質因數 + 高精度
@[高精度, Prufer, 質因數分解]
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为\(N(0 < N <= 1000)\),
接下来\(N\)行,第\(i + 1\)行给出第\(i\)个节点的度数\(D_i\),如果对度数不要求,则输入\(- 1\)
Output
一个整数,表示不同的满足要求的树的个数,无解输出\(0\)
Sample Input
3
1
-1
-1
Sample Output
2
HINT
两棵树分别为1-2-3; 1-3-2
Solution
Prufer編碼的簡單應用.
Prufer是無根樹的一種編碼, 可以和無根樹之間形成一一對應關係. 生成Prufer編碼的方法很簡單, 大致過程如下(這都不是重點)
一种生成Prufer序列的方法是迭代删点,直到原图仅剩两个点。对于一棵顶点已经经过编号的树T,顶点的编号为{1,2,...,n},在第i步时,移去所有叶子节点(度为1的顶点)中标号最小的顶点和相连的边,并把与它相邻的点的编号加入Prufer序列中,重复以上步骤直到原图仅剩2个顶点。以右边的树为例子,首先在所有叶子节点中编号最小的点是2,和它相邻的点的编号是3,将3加入序列并删除编号为2的点。接下来删除的点是4,5被加入序列,然后删除5,1,此时原图仅剩两个点,Prufer序列构建完成,为{3,5,1,3}
重點在於: 在Prufer編碼中, $$一個節點出現的次數 = 該點的度 - 1$$
有了這個性質, 這題就可以簡單地轉化為求Prufer編碼的排列組合數量.
【思考】怎么计算呢?就用排列组合的原理。我们先考虑不是-1的点。一共有N-2个格子,设当前的点要求为a1(已经减1了),我们可以放的总数是:C(N-2,a1)。这样占了a1个格子。下一个a2的情况就是C(N-2-a1,a2)。依次类推。
然后是-1的情况,我想了很长时间。
原来是这样想的:设最后还有K个格子,且有SUM个-1。我们用DP思路。f[i][j]表示放到最后K个格子中的第i个格子时,放第j种颜色(注意颜色是不下降的,最后只需乘上阶乘即可)的情况数。但是因为要用高精,DP会很麻烦。
感谢HHD大牛的指导:其实就是SUM^K。为什么?对于每一个格子,我们都可以放SUM种情况~~~~。
【优化】为了不超时,用质数表来优化。
實際上就是用分解質因數的方法來優化, 然後再用高精度計算最終答案.
#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
inline int read()
{
int x = 0, flag = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
flag *= - 1;
while(isdigit(c))
x = x * 10 + c - '0', c = getchar();
return x * flag;
}
void println(int x)
{
if(x < 0)
putchar('-'), x *= - 1;
if(x == 0)
putchar('0');
int ans[10], top = 0;
while(x)
ans[top ++] = x % 10, x /= 10;
for(; top; top --)
putchar(ans[top - 1] + '0');
putchar('\n');
}
const int N = 1 << 10;
int top;
int isprime[N], prime[N];
void get_prime()
{
memset(isprime, - 1, sizeof(isprime));
top = 0;
for(int i = 2; i < N; i ++)
if(isprime[i] == - 1)
{
isprime[i] = 1, prime[top ++] = i;
for(int j = i << 1; j < N; j += i)
isprime[j] = 0;
}
}
int ans[N];
void C(int n, int m)
{
for(int i = n; i > n - m; i --)
{
int p = i;
for(int j = 0; j < top; j ++)
while(! (p % prime[j]))
ans[j] ++, p /= prime[j];
}
for(int i = m; i; i --)
{
int p = i;
for(int j = 0; j < top; j ++)
while(! (p % prime[j]))
ans[j] --, p /= prime[j];
}
}
struct Giant
{
int dig[1 << 20];
int top;
Giant()
{
top = 1;
memset(dig, 0, sizeof(dig));
dig[0] = 1;
}
};
void operator *(Giant &x, int y)
{
for(int i = 0; i < x.top; i ++)
x.dig[i] *= y;
for(int i = 0; i < x.top; i ++)
if(x.dig[i] > 9)
x.dig[i + 1] += x.dig[i] / 10, x.dig[i] %= 10;
while(x.dig[x.top])
x.dig[x.top + 1] = x.dig[x.top] / 10,
x.dig[x.top] %= 10,
x.top ++;
}
void println(Giant &x)
{
for(int i = x.top; i; i --)
putchar(x.dig[i - 1] + '0');
putchar('\n');
}
Giant Ans;
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ1005.in", "r", stdin);
freopen("BZOJ1005.out", "w", stdout);
#endif
get_prime();
int n = read();
int len = 0, cnt = 0;
//len記錄prufer序列已經佔用的長度, cnt記錄尚未確定度的點數
for(int i = 0; i < n; i ++)
{
int D = read();
if((! D) || (D - 1 + len > n - 2))
{
println(0);
return 0;
}
if(D == - 1)
cnt ++;
if(D > 0)
C(n - 2 - len, D - 1), len += D - 1;
}
if(len < n - 2)
{
for(int i = 0; i < top; i ++)
while(! (cnt % prime[i]))
ans[i] += n - 2 - len, cnt /= prime[i];
}
for(int i = 0; i < top; i ++)
for(int j = 0; j < ans[i]; j ++)
Ans * prime[i];
println(Ans);
}
BZOJ1005明明的烦恼 Prufer + 分解質因數 + 高精度的更多相关文章
- bzoj1211树的计数 x bzoj1005明明的烦恼 题解(Prufer序列)
1211: [HNOI2004]树的计数 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3432 Solved: 1295[Submit][Stat ...
- bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数
1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 2248 Solved: 898[Submit][Statu ...
- BZOJ 1005 [HNOI2008]明明的烦恼 (Prufer编码 + 组合数学 + 高精度)
1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 5786 Solved: 2263[Submit][Stat ...
- bzoj 1005 [HNOI2008] 明明的烦恼 (prufer编码)
[HNOI2008]明明的烦恼 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 5907 Solved: 2305[Submit][Status][Di ...
- [BZOJ1005] [HNOI2008] 明明的烦恼 (prufer编码)
Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N ...
- 【bzoj1005】[HNOI2008]明明的烦恼 Prufer序列+高精度
题目描述 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? 输入 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i ...
- [bzoj1005][HNOI2008]明明的烦恼-Prufer编码+高精度
Brief Description 给出标号为1到N的点,以及某些点最终的度数,允许在 任意两点间连线,可产生多少棵度数满足要求的树? Algorithm Design 结论题. 首先可以参考这篇文章 ...
- bzoj1005: [HNOI2008]明明的烦恼 prufer序列
https://www.lydsy.com/JudgeOnline/problem.php?id=1005 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的 ...
- BZOJ.1005.[HNOI2008]明明的烦恼(Prufer 高精 排列组合)
题目链接 若点数确定那么ans = (n-2)!/[(d1-1)!(d2-1)!...(dn-1)!] 现在把那些不确定的点一起考虑(假设有m个),它们在Prufer序列中总出现数就是left=n-2 ...
随机推荐
- Letters CodeForces - 978C (二分)
Time limit4000 ms Memory limit262144 kB There are nn dormitories in Berland State University, they a ...
- HDU - 5884 Sort (二分答案+贪心)
有n个数字,你需要把这n个数字合成一个数字,每次只能把k个数字合并成一个,花费为这k个数字的和. 给一个最大花费,问不超过这个最大花费的情况下,k的最小值. Sample Input 1 5 25 1 ...
- UVa 12299 线段树 单点更新 RMQ with Shifts
因为shift操作中的数不多,所以直接用单点更新模拟一下就好了. 太久不写线段树,手好生啊,不是这错一下就是那错一下. PS:输入写的我有点蛋疼,不知道谁有没有更好的写法. #include < ...
- __block 和__weak
1,在MRC 时代,__block 修饰,可以避免循环引用:ARC时代,__block 修饰,同样会引起循环引用问题: 2,__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修 ...
- windows :Tomcat免安装版环境变量配置 + jdk配置
1. 下载后解压,我解压的目录为:D:\Tomcat\apache-tomcat-9.0.1-windows-x64 2. 安装jdk和jre, 并配置环境变量: 2.1 用户变量新建JAVA_H ...
- 【NOIP2016】愤怒的小鸟 搜索
题目描述 Kiana 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 (0,0)(0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟,小 ...
- (英文排版测试)Lorem Ipsum
Lorem Ipsum Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis tincidunt consequat pretiu ...
- 指定特殊的安装目录用configure进行配置
linux - Make install, but not to default directories? - Stack Overflow I want to run 'make install' ...
- failed to allocate for range 0: no IP addresses available in range set: 172.20.xx.1-172.20.xx.254
今天遇到一个机器上的Pod 在创建以后一直处于Init 0/1的状态,进到这个节点查看其kubelet的状态,发现果然有问题 systemctl status kubelet .go:] Contai ...
- IE6 IE7下li间距、高度不一致问题(转)
http://www.phpddt.com/dhtml/926.html 问题描述:li的高度在IE6 IE7间距高度和其他浏览器不一致,即便设定了高度,IE6,7中,仍比其他浏览器要高. 解决方法: ...