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 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标 ...
随机推荐
- Android开发_字符串处理类-TextUtils类
对于字符串处理Android为我们提供了一个简单实用的TextUtils类,如果处理比较简单的内容不用去思考正则表达式不妨试试这个在android.text.TextUtils的类,主要的功能如下: ...
- cocos2dx游戏项目总结(持续更新)
1.在一个layer里面,尽量只使用一种按钮类型.如MenuItem or CCControlButton.因为这两种按钮的优先级不同,在使用过程中会互相影响到事件触发的先后顺序. 2.细节的问题要一 ...
- ObjectInputStream ObjectOutStream
1:不能多次read 2:read 与write操作必须一对一
- Android开发之ViewPager实现多页面切换及动画效果(仿Android的Launcher效果)
Android开发中经常会有引导页或者切换页面等效果,本文采用ViewPager结合动画效果来实现仿Launcher以及页面切换的效果.源码地址在文章最后给出下载. 效果图如下: 1.Vi ...
- System Operations on AWS - Lab 6W - Using Auto Scaling (Windows)
创建你的一个web server,然后将这个实例制成你的AMI,通过启动配置生成一个Auto Scaling组(包括scale-in/scale-out策略),配置一台Load Balancer指向你 ...
- 需要重新启动计算机.必须重新启动计算机才能安装 SQL Server
在开始运行中输入regedit找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager 在右边窗口找到PendingFi ...
- C# 事件的理解
说实话,事件弄得还是很晕,有待于以后的强化吧,下面是我对事件的一点理解 首先,参见大牛的帖子:网上大牛事件讲解 下面我来说一说事件的大致流程: 事件委托事件概述事件就是当对象或类状态发生改变时,对象或 ...
- BI任务列表
了解点击流系统和pv/uv的相关计算 关于pv的那些事!! ···············································2014-09-10 homework做了些什 ...
- 什么是JavaScript?
- [视频转换] C#VideoConvert视频转换帮助类 (转载)
点击下载 VideoConvert.zip 主要功能如下 .获取文件的名字 .获取文件扩展名 .获取文件类型 .视频格式转为Flv .生成Flv视频的缩略图 .转换文件并保存在指定文件夹下 .转换文件 ...