bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数
1005: [HNOI2008]明明的烦恼
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 2248 Solved: 898
[Submit][Status]
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
一个整数,表示不同的满足要求的树的个数,无解输出0
Sample Input
1
-1
-1
Sample Output
HINT
两棵树分别为1-2-3;1-3-2
Source
生成树prufer标号
首先选这棵树叶子中编号最小的点,将这个点删除,并且把它的邻接点加入一个数组中,例如第一个删除的节点为1,并且把5加入数组中。删除节点后形成一棵新的树,再在新树中删除最小的节点,并且把邻接点加入数组中,,这样重复以上步骤,知道树中最后剩余两个点的时候终止操作。这时候数组中的便是prufer编码。
生成树prufer标号的性质(觉得相当神奇啊):
1.prufer序列和树一一对应。
2.序列的长度=树的节点个数-2。
3.树中该节点的度数=该节点编号在序列中出现的次数+1,叶子节点不会出现在序列中。
每一个标号有且仅有一个对应的生成树。
有些时候当计数问题不含加减,但含除法时,可通过分解质因数简化。
而且光靠想出来的公式很不靠谱的。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1010
#define MAXL 1000
#define VAL1 10000
class number//四位
{
public:
number()
{
clear();
}
bool is_odd()
{
return numb[]%==;
}
bool is_even()
{
return numb[]%==;
}
void lsh_bin()
{
int i;
for (i=topn;i>;i--)
{
if (numb[i]%==)
{
numb[i-]+=VAL1;
}
numb[i]/=;
}
numb[]/=;
while (topn&&!numb[topn])topn--;
}
bool equal_to(int x)
{
if (topn==)
{
return x==numb[];
}
if (topn==)
{
return x==numb[]+numb[]*VAL1;
}
return false;
}
int size()
{
return topn;
}
int length()
{
int x=numb[topn];
int ret=;
while (x)
{
ret++;
x/=;
}
int y=;
x=VAL1;
while (x)
{
y++;
x/=;
}
y--;
ret+=topn*y;
return ret;
}
void operator =(int x)//{{{
{
int now=;
clear();
numb[now]=x;
while (numb[now]>=VAL1)
{
numb[now+]+=numb[now]/VAL1;
numb[now]%=VAL1;
now++;
if (now>topn)topn=now;
}
}//}}}
void operator =(number num)//{{{
{
topn=num.topn;
memcpy((this->numb),num.numb,sizeof(num.numb[])*(topn+));
}//}}}
void operator +=(number &num)//{{{
{
int i;
topn=max(topn,num.topn);
for (i=;i<=topn;i++)
{
numb[i]+=num.numb[i];;
if (numb[i]>=VAL1)
{
numb[i+]+=numb[i]/VAL1;
numb[i]%=VAL1;
}
}
while (numb[topn+])
{
topn++;
numb[topn+]+=numb[topn]/VAL1;
numb[topn]%=VAL1;
}
}//}}}
void operator +=(int x)//{{{
{
int now=;
if (topn==-)topn=;
numb[now]+=x;
while (numb[now]>=VAL1)
{
numb[now+]+=numb[now]/VAL1;
numb[now]%=VAL1;
now++;
if (now>topn)topn=now;
}
}//}}}
void operator *=(int x)//{{{
{
int i;
for (i=;i<=topn;i++)
{
numb[i]*=x;
}
for (i=;i<=topn;i++)
{
if (numb[i]>=VAL1)
{
numb[i+]+=numb[i]/VAL1;
numb[i]%=VAL1;
}
}
while (numb[topn+])
{
topn++;
numb[topn+]+=numb[topn]/VAL1;
numb[topn]%=VAL1;
}
}//}}}
void operator *=(number &num)
{
number ret;
ret=(*this)*num;
(*this)=ret;
}
void operator -=(number &num)//{{{
{
if (*this<num)throw "Error!\n->void operator -=(number &num)\n";
int i;
for (i=;i<=topn;i++)
{
numb[i]-=num.numb[i];
}
for (i=;i<=topn;i++)
{
while (numb[i]<)
{
numb[i]+=VAL1;
numb[i+]--;
}
}
while (topn&&!numb[topn])topn--;
}//}}}
void operator /=(int x)
{
int i;
int tot=;
for (i=topn;i>=;i--)
{
tot=tot*VAL1+numb[i];
numb[i]=tot/x;
tot%=x;
}
while (topn&&!numb[topn])topn--;
}
void operator --(int)//{{{
{
if (topn==&&numb[]==)throw "Error!\n->void operator --(int)\n";
int now=;
numb[now]--;
while (numb[now]<)
{
numb[now+]--;
numb[now]+=VAL1;
}
while (topn&&!numb[topn])topn--;
}//}}}
private:
int numb[MAXL];
int topn;
void clear()
{
topn=;
memset(numb,,sizeof(numb)); }
friend bool operator <(number num1,number num2);
friend bool operator <=(number num1,number num2);
friend bool operator ==(number num1,number num2);
friend ostream& operator <<(ostream &out,number &num);
friend istream& operator >>(istream &in,number &num);
friend number operator *(number &num1,number &num2);
friend number operator *(number num,int x);
friend number operator +(number num1,number num2);
friend number operator +(number num1,int x);
friend number operator -(number num1,number num2);
//a=a+b远没有a+=b快
};
bool operator <(number num1,number num2)//{{{
{
if (num1.topn!=num2.topn)
{
return num1.topn<num2.topn;
}
int i;
for (i=num1.topn;i>=;i--)
{
if (num1.numb[i]!=num2.numb[i])
{
return num1.numb[i]<num2.numb[i];
}
}
return false;
}//}}}
bool operator <=(number num1,number num2)//{{{
{
if (num1.topn!=num2.topn)
{
return num1.topn<num2.topn;
}
int i;
for (i=num1.topn;i>=;i--)
{
if (num1.numb[i]!=num2.numb[i])
{
return num1.numb[i]<num2.numb[i];
}
}
return true;
}//}}}
bool operator ==(number num1,number num2)//{{{
{
if (num1.topn!=num2.topn)return false;
for (int i=;i<=num1.topn;i++)
{
if (num1.numb[i]!=num2.numb[i])return false;
}
return true;
}//}}}
ostream& operator <<(ostream &out,number &num)//{{{
{
int i;
out<<num.numb[num.topn];
for (i=num.topn-;i>=;i--)
{
//压六位时
// if (num.numb[i]<100000)out<<"0";
// if (num.numb[i]<10000)out<<"0";
if (num.numb[i]<)out<<"";
if (num.numb[i]<)out<<"";
if (num.numb[i]<)out<<"";
out<<num.numb[i];
}
return out;
}//}}}
istream& operator >>(istream &in,number &num)//{{{
{
string str;
in>>str;
int i;
num.clear();
for (i=(int)str.length()-,num.topn=;i>=;i-=,num.topn++)
{
if (i-<str.length())
{
num.numb[num.topn]=(str[i]-'')+*(str[i-]-'')+*(str[i-]-'')+*(str[i-]-'');
}else
{
if (i-<str.length())num.numb[num.topn]+=*(str[i-]-'');
if (i-<str.length())num.numb[num.topn]+=*(str[i-]-'');
if (i <str.length())num.numb[num.topn]+=(str[i]-'');
}
}
num.topn--;
return in;
}//}}}
number operator *(number num,int x)//{{{
{
number ret;
ret=num;
ret*=x;
return ret;
}//}}}
number operator * (number &num1,number &num2)
{
int i,j;
number ret;
ret.topn=num1.topn+num2.topn;
for (i=;i<=num1.topn;i++)
{
for (j=;j<=num2.topn;j++)
{
ret.numb[i+j]+=num1.numb[i]*num2.numb[j];
if (ret.numb[i+j]>=VAL1)
{
ret.numb[i+j+]+=ret.numb[i+j]/VAL1;
ret.numb[i+j]%=VAL1;
}
}
}
for (i=;i<=ret.topn;i++)
{
if (ret.numb[i]>=VAL1)
{
ret.numb[i+]+=ret.numb[i]/VAL1;
ret.numb[i]%=VAL1;
}
}
while (ret.numb[ret.topn+])
{
ret.topn++;
ret.numb[ret.topn+]+=ret.numb[ret.topn]/VAL1;
ret.numb[ret.topn]%=VAL1;
}
return ret;
}
number operator +(number num1,number num2)//{{{
{
number ret;
ret=num1;
ret+=num2;
return ret;
}//}}}
number operator +(number num1,int x)//{{{
{
number ret;
ret=num1;
ret+=x;
return ret;
}//}}}
number operator -(number num1,number num2)//{{{
{
number ret;
ret=num1;
ret-=num2;
return ret;
}//}}}
number c (int x,int y)
{
number ret;
ret=;
int i;
for (i=y+;i<=x;i++)
ret*=i;
for (i=;i<=(x-y);i++)
ret/=i;
return ret;
}
int main()
{
freopen("input.txt","r",stdin);
int x;
int n;
scanf("%d",&n);
number ans;
number temp1,temp2,temp3;
// number temp;
// temp=c(1000,2);
// cout<<temp<<endl;
ans=;
int r=n-;
int i,j;
j=;
int totu=;
for (i=;i<n;i++)
{
scanf("%d",&x);
if (x==)
{
printf("0\n");
return ;
}
if (x!=-)
{
if (!r&&x-)
{
printf("0\n");
return ;
}
temp1=c(r,x-);
ans*=temp1;
r-=x-;
if (r<)
{
printf("0\n");
return ;
}
}else
{
totu++;
}
}
if (r&&totu<=)
{
printf("0\n");
return ;
}
if (r)
{
for (i=;i<=r;i++)
{
ans=ans*totu;
}
}
cout<<ans<<endl;
}
bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数的更多相关文章
- 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 ...
- BZOJ 1005 [HNOI2008]明明的烦恼 ★(Prufer数列)
题意 N个点,有些点有度数限制,问这些点可以构成几棵不同的树. 思路 [Prufer数列] Prufer数列是无根树的一种数列.在组合数学中,Prufer数列是由一个对于顶点标过号的树转化来的数列,点 ...
- BZOJ.1005.[HNOI2008]明明的烦恼(Prufer 高精 排列组合)
题目链接 若点数确定那么ans = (n-2)!/[(d1-1)!(d2-1)!...(dn-1)!] 现在把那些不确定的点一起考虑(假设有m个),它们在Prufer序列中总出现数就是left=n-2 ...
- BZOJ 1005 [HNOI2008] 明明的烦恼(组合数学 Purfer Sequence)
题目大意 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为 1 到 N 的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为 N( ...
- BZOJ 1005: [HNOI2008]明明的烦恼( 组合数学 + 高精度 )
首先要知道一种prufer数列的东西...一个prufer数列和一颗树对应..然后树上一个点的度数-1是这个点在prufer数列中出现次数..这样就转成一个排列组合的问题了.算个可重集的排列数和组合数 ...
- BZOJ 1005: [HNOI2008]明明的烦恼 Purfer序列 大数
1005: [HNOI2008]明明的烦恼 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/ ...
- BZOJ 1005 [HNOI2008]明明的烦恼 purfer序列,排列组合
1005: [HNOI2008]明明的烦恼 Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少 ...
- BZOJ 1005: [HNOI2008]明明的烦恼(prufer数列)
http://www.lydsy.com/JudgeOnline/problem.php?id=1005 题意: Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标 ...
随机推荐
- careercup-链表 2.4
2.4 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前. 思路:将小于的结点还是保存在原来的链表中,将大于等于x的结点加入一个新的链表,最后将这两个链表链接起 ...
- 自定义String类,并且实现在STL容器中添加自定义的类型
13.44 编写标准库string类的简化版本,命名String.你的类应该至少有一个默认构造函数和一个接受C风格字符串指针参数的构造函数.使用allocator为你的String类分配所需内存. 1 ...
- getline和get的区别
#include<iostream> #include<fstream> #include<cstring> using namespace std; int ma ...
- 为centos6.5系统添加epel源
1.进去http://fedoraproject.org/wiki/EPEL 2.因为系统是centos 6.5,所以获取epel-release-latest-6.noarch.rpm的地址(htt ...
- 【Android】数据存储-java IO流文件存储
1.数据持久化:将在内存中的瞬时数据保存在存储设备中.瞬时数据:设备关机数据丢失.持久化技术提供一种机制可以让数据在瞬时状态和持久状态之间转换. 2.Android中简单的三种存储方式:文件存储.Sh ...
- hibernate自动建表采用UTF-8字符编码
hibernate自动建表采用UTF-8字符编码 hibernate建表默认为UTF-8编码 >>>>>>>>>>>>>& ...
- less编码规范
Less 编码规范 简介 因为自己最近写css用的比较多还是less,整理了一份less规范, 代码组织 代码按如下形式按顺序组织: @import 变量声明 样式声明 // ✓ @import &q ...
- bootstrap3.0 模态框显示的文字超出怎么办
版本:bootstrap3.3 模态框文字超出 解决方案: 在html中控制自动换行 其实只要在表格控制中添加一句<div style="word-break:break-all& ...
- HierarchicalDataBoundControl 错误
出现以上错误原因是控件Datasources绑定出错,可能原因是没有区分树形结构的控件如Treeview的绑定与二维数据如datagridview绑定之间的区别.
- Windows下的进程【一】
什么是进程?进程就是一个正在运行的程序的实例,由两部分组成: 内核对象.操作系统用内核对象对进程进行管理,内核对象是操作系统保存进程统计信息的地方. 地址空间.其中包含所有可执行文件或DLL模块的代码 ...