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

3
1
-1
-1

Sample Output

2

HINT

两棵树分别为1-2-3;1-3-2

Source

生成树prufer标号

  首先选这棵树叶子中编号最小的点,将这个点删除,并且把它的邻接点加入一个数组中,例如第一个删除的节点为1,并且把5加入数组中。删除节点后形成一棵新的树,再在新树中删除最小的节点,并且把邻接点加入数组中,,这样重复以上步骤,知道树中最后剩余两个点的时候终止操作。这时候数组中的便是prufer编码。

生成树prufer标号的性质(觉得相当神奇啊):

1.prufer序列和树一一对应。

2.序列的长度=树的节点个数-2。

3.树中该节点的度数=该节点编号在序列中出现的次数+1,叶子节点不会出现在序列中。

每一个标号有且仅有一个对应的生成树。

有些时候当计数问题不含加减,但含除法时,可通过分解质因数简化。

而且光靠想出来的公式很不靠谱的。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. #define MAXN 1010
  7. #define MAXL 1000
  8. #define VAL1 10000
  9. class number//四位
  10. {
  11. public:
  12. number()
  13. {
  14. clear();
  15. }
  16. bool is_odd()
  17. {
  18. return numb[]%==;
  19. }
  20. bool is_even()
  21. {
  22. return numb[]%==;
  23. }
  24. void lsh_bin()
  25. {
  26. int i;
  27. for (i=topn;i>;i--)
  28. {
  29. if (numb[i]%==)
  30. {
  31. numb[i-]+=VAL1;
  32. }
  33. numb[i]/=;
  34. }
  35. numb[]/=;
  36. while (topn&&!numb[topn])topn--;
  37. }
  38. bool equal_to(int x)
  39. {
  40. if (topn==)
  41. {
  42. return x==numb[];
  43. }
  44. if (topn==)
  45. {
  46. return x==numb[]+numb[]*VAL1;
  47. }
  48. return false;
  49. }
  50. int size()
  51. {
  52. return topn;
  53. }
  54. int length()
  55. {
  56. int x=numb[topn];
  57. int ret=;
  58. while (x)
  59. {
  60. ret++;
  61. x/=;
  62. }
  63. int y=;
  64. x=VAL1;
  65. while (x)
  66. {
  67. y++;
  68. x/=;
  69. }
  70. y--;
  71. ret+=topn*y;
  72. return ret;
  73. }
  74. void operator =(int x)//{{{
  75. {
  76. int now=;
  77. clear();
  78. numb[now]=x;
  79. while (numb[now]>=VAL1)
  80. {
  81. numb[now+]+=numb[now]/VAL1;
  82. numb[now]%=VAL1;
  83. now++;
  84. if (now>topn)topn=now;
  85. }
  86. }//}}}
  87. void operator =(number num)//{{{
  88. {
  89. topn=num.topn;
  90. memcpy((this->numb),num.numb,sizeof(num.numb[])*(topn+));
  91. }//}}}
  92. void operator +=(number &num)//{{{
  93. {
  94. int i;
  95. topn=max(topn,num.topn);
  96. for (i=;i<=topn;i++)
  97. {
  98. numb[i]+=num.numb[i];;
  99. if (numb[i]>=VAL1)
  100. {
  101. numb[i+]+=numb[i]/VAL1;
  102. numb[i]%=VAL1;
  103. }
  104. }
  105. while (numb[topn+])
  106. {
  107. topn++;
  108. numb[topn+]+=numb[topn]/VAL1;
  109. numb[topn]%=VAL1;
  110. }
  111. }//}}}
  112. void operator +=(int x)//{{{
  113. {
  114. int now=;
  115. if (topn==-)topn=;
  116. numb[now]+=x;
  117. while (numb[now]>=VAL1)
  118. {
  119. numb[now+]+=numb[now]/VAL1;
  120. numb[now]%=VAL1;
  121. now++;
  122. if (now>topn)topn=now;
  123. }
  124. }//}}}
  125. void operator *=(int x)//{{{
  126. {
  127. int i;
  128. for (i=;i<=topn;i++)
  129. {
  130. numb[i]*=x;
  131. }
  132. for (i=;i<=topn;i++)
  133. {
  134. if (numb[i]>=VAL1)
  135. {
  136. numb[i+]+=numb[i]/VAL1;
  137. numb[i]%=VAL1;
  138. }
  139. }
  140. while (numb[topn+])
  141. {
  142. topn++;
  143. numb[topn+]+=numb[topn]/VAL1;
  144. numb[topn]%=VAL1;
  145. }
  146. }//}}}
  147. void operator *=(number &num)
  148. {
  149. number ret;
  150. ret=(*this)*num;
  151. (*this)=ret;
  152. }
  153. void operator -=(number &num)//{{{
  154. {
  155. if (*this<num)throw "Error!\n->void operator -=(number &num)\n";
  156. int i;
  157. for (i=;i<=topn;i++)
  158. {
  159. numb[i]-=num.numb[i];
  160. }
  161. for (i=;i<=topn;i++)
  162. {
  163. while (numb[i]<)
  164. {
  165. numb[i]+=VAL1;
  166. numb[i+]--;
  167. }
  168. }
  169. while (topn&&!numb[topn])topn--;
  170. }//}}}
  171. void operator /=(int x)
  172. {
  173. int i;
  174. int tot=;
  175. for (i=topn;i>=;i--)
  176. {
  177. tot=tot*VAL1+numb[i];
  178. numb[i]=tot/x;
  179. tot%=x;
  180. }
  181. while (topn&&!numb[topn])topn--;
  182. }
  183. void operator --(int)//{{{
  184. {
  185. if (topn==&&numb[]==)throw "Error!\n->void operator --(int)\n";
  186. int now=;
  187. numb[now]--;
  188. while (numb[now]<)
  189. {
  190. numb[now+]--;
  191. numb[now]+=VAL1;
  192. }
  193. while (topn&&!numb[topn])topn--;
  194. }//}}}
  195. private:
  196. int numb[MAXL];
  197. int topn;
  198. void clear()
  199. {
  200. topn=;
  201. memset(numb,,sizeof(numb));
  202.  
  203. }
  204. friend bool operator <(number num1,number num2);
  205. friend bool operator <=(number num1,number num2);
  206. friend bool operator ==(number num1,number num2);
  207. friend ostream& operator <<(ostream &out,number &num);
  208. friend istream& operator >>(istream &in,number &num);
  209. friend number operator *(number &num1,number &num2);
  210. friend number operator *(number num,int x);
  211. friend number operator +(number num1,number num2);
  212. friend number operator +(number num1,int x);
  213. friend number operator -(number num1,number num2);
  214. //a=a+b远没有a+=b快
  215. };
  216. bool operator <(number num1,number num2)//{{{
  217. {
  218. if (num1.topn!=num2.topn)
  219. {
  220. return num1.topn<num2.topn;
  221. }
  222. int i;
  223. for (i=num1.topn;i>=;i--)
  224. {
  225. if (num1.numb[i]!=num2.numb[i])
  226. {
  227. return num1.numb[i]<num2.numb[i];
  228. }
  229. }
  230. return false;
  231. }//}}}
  232. bool operator <=(number num1,number num2)//{{{
  233. {
  234. if (num1.topn!=num2.topn)
  235. {
  236. return num1.topn<num2.topn;
  237. }
  238. int i;
  239. for (i=num1.topn;i>=;i--)
  240. {
  241. if (num1.numb[i]!=num2.numb[i])
  242. {
  243. return num1.numb[i]<num2.numb[i];
  244. }
  245. }
  246. return true;
  247. }//}}}
  248. bool operator ==(number num1,number num2)//{{{
  249. {
  250. if (num1.topn!=num2.topn)return false;
  251. for (int i=;i<=num1.topn;i++)
  252. {
  253. if (num1.numb[i]!=num2.numb[i])return false;
  254. }
  255. return true;
  256. }//}}}
  257. ostream& operator <<(ostream &out,number &num)//{{{
  258. {
  259. int i;
  260. out<<num.numb[num.topn];
  261. for (i=num.topn-;i>=;i--)
  262. {
  263. //压六位时
  264. // if (num.numb[i]<100000)out<<"0";
  265. // if (num.numb[i]<10000)out<<"0";
  266. if (num.numb[i]<)out<<"";
  267. if (num.numb[i]<)out<<"";
  268. if (num.numb[i]<)out<<"";
  269. out<<num.numb[i];
  270. }
  271. return out;
  272. }//}}}
  273. istream& operator >>(istream &in,number &num)//{{{
  274. {
  275. string str;
  276. in>>str;
  277. int i;
  278. num.clear();
  279. for (i=(int)str.length()-,num.topn=;i>=;i-=,num.topn++)
  280. {
  281. if (i-<str.length())
  282. {
  283. num.numb[num.topn]=(str[i]-'')+*(str[i-]-'')+*(str[i-]-'')+*(str[i-]-'');
  284. }else
  285. {
  286. if (i-<str.length())num.numb[num.topn]+=*(str[i-]-'');
  287. if (i-<str.length())num.numb[num.topn]+=*(str[i-]-'');
  288. if (i <str.length())num.numb[num.topn]+=(str[i]-'');
  289. }
  290. }
  291. num.topn--;
  292. return in;
  293. }//}}}
  294. number operator *(number num,int x)//{{{
  295. {
  296. number ret;
  297. ret=num;
  298. ret*=x;
  299. return ret;
  300. }//}}}
  301. number operator * (number &num1,number &num2)
  302. {
  303. int i,j;
  304. number ret;
  305. ret.topn=num1.topn+num2.topn;
  306. for (i=;i<=num1.topn;i++)
  307. {
  308. for (j=;j<=num2.topn;j++)
  309. {
  310. ret.numb[i+j]+=num1.numb[i]*num2.numb[j];
  311. if (ret.numb[i+j]>=VAL1)
  312. {
  313. ret.numb[i+j+]+=ret.numb[i+j]/VAL1;
  314. ret.numb[i+j]%=VAL1;
  315. }
  316. }
  317. }
  318. for (i=;i<=ret.topn;i++)
  319. {
  320. if (ret.numb[i]>=VAL1)
  321. {
  322. ret.numb[i+]+=ret.numb[i]/VAL1;
  323. ret.numb[i]%=VAL1;
  324. }
  325. }
  326. while (ret.numb[ret.topn+])
  327. {
  328. ret.topn++;
  329. ret.numb[ret.topn+]+=ret.numb[ret.topn]/VAL1;
  330. ret.numb[ret.topn]%=VAL1;
  331. }
  332. return ret;
  333. }
  334. number operator +(number num1,number num2)//{{{
  335. {
  336. number ret;
  337. ret=num1;
  338. ret+=num2;
  339. return ret;
  340. }//}}}
  341. number operator +(number num1,int x)//{{{
  342. {
  343. number ret;
  344. ret=num1;
  345. ret+=x;
  346. return ret;
  347. }//}}}
  348. number operator -(number num1,number num2)//{{{
  349. {
  350. number ret;
  351. ret=num1;
  352. ret-=num2;
  353. return ret;
  354. }//}}}
  355. number c (int x,int y)
  356. {
  357. number ret;
  358. ret=;
  359. int i;
  360. for (i=y+;i<=x;i++)
  361. ret*=i;
  362. for (i=;i<=(x-y);i++)
  363. ret/=i;
  364. return ret;
  365. }
  366. int main()
  367. {
  368. freopen("input.txt","r",stdin);
  369. int x;
  370. int n;
  371. scanf("%d",&n);
  372. number ans;
  373. number temp1,temp2,temp3;
  374. // number temp;
  375. // temp=c(1000,2);
  376. // cout<<temp<<endl;
  377. ans=;
  378. int r=n-;
  379. int i,j;
  380. j=;
  381. int totu=;
  382. for (i=;i<n;i++)
  383. {
  384. scanf("%d",&x);
  385. if (x==)
  386. {
  387. printf("0\n");
  388. return ;
  389. }
  390. if (x!=-)
  391. {
  392. if (!r&&x-)
  393. {
  394. printf("0\n");
  395. return ;
  396. }
  397. temp1=c(r,x-);
  398. ans*=temp1;
  399. r-=x-;
  400. if (r<)
  401. {
  402. printf("0\n");
  403. return ;
  404. }
  405. }else
  406. {
  407. totu++;
  408. }
  409. }
  410. if (r&&totu<=)
  411. {
  412. printf("0\n");
  413. return ;
  414. }
  415. if (r)
  416. {
  417. for (i=;i<=r;i++)
  418. {
  419. ans=ans*totu;
  420. }
  421. }
  422. cout<<ans<<endl;
  423. }

bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数的更多相关文章

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

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

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

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

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

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

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

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

  5. BZOJ 1005 [HNOI2008] 明明的烦恼(组合数学 Purfer Sequence)

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

  6. BZOJ 1005: [HNOI2008]明明的烦恼( 组合数学 + 高精度 )

    首先要知道一种prufer数列的东西...一个prufer数列和一颗树对应..然后树上一个点的度数-1是这个点在prufer数列中出现次数..这样就转成一个排列组合的问题了.算个可重集的排列数和组合数 ...

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

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

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

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

  9. BZOJ 1005: [HNOI2008]明明的烦恼(prufer数列)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1005 题意: Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标 ...

随机推荐

  1. Android开发_字符串处理类-TextUtils类

    对于字符串处理Android为我们提供了一个简单实用的TextUtils类,如果处理比较简单的内容不用去思考正则表达式不妨试试这个在android.text.TextUtils的类,主要的功能如下: ...

  2. cocos2dx游戏项目总结(持续更新)

    1.在一个layer里面,尽量只使用一种按钮类型.如MenuItem or CCControlButton.因为这两种按钮的优先级不同,在使用过程中会互相影响到事件触发的先后顺序. 2.细节的问题要一 ...

  3. ObjectInputStream ObjectOutStream

    1:不能多次read 2:read 与write操作必须一对一

  4. Android开发之ViewPager实现多页面切换及动画效果(仿Android的Launcher效果)

    Android开发中经常会有引导页或者切换页面等效果,本文采用ViewPager结合动画效果来实现仿Launcher以及页面切换的效果.源码地址在文章最后给出下载. 效果图如下:       1.Vi ...

  5. System Operations on AWS - Lab 6W - Using Auto Scaling (Windows)

    创建你的一个web server,然后将这个实例制成你的AMI,通过启动配置生成一个Auto Scaling组(包括scale-in/scale-out策略),配置一台Load Balancer指向你 ...

  6. 需要重新启动计算机.必须重新启动计算机才能安装 SQL Server

    在开始运行中输入regedit找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager 在右边窗口找到PendingFi ...

  7. C# 事件的理解

    说实话,事件弄得还是很晕,有待于以后的强化吧,下面是我对事件的一点理解 首先,参见大牛的帖子:网上大牛事件讲解 下面我来说一说事件的大致流程: 事件委托事件概述事件就是当对象或类状态发生改变时,对象或 ...

  8. BI任务列表

    了解点击流系统和pv/uv的相关计算 关于pv的那些事!! ···············································2014-09-10 homework做了些什 ...

  9. 什么是JavaScript?

  10. [视频转换] C#VideoConvert视频转换帮助类 (转载)

    点击下载 VideoConvert.zip 主要功能如下 .获取文件的名字 .获取文件扩展名 .获取文件类型 .视频格式转为Flv .生成Flv视频的缩略图 .转换文件并保存在指定文件夹下 .转换文件 ...