BZOJ1005HNOI2008明明的烦恼

Posted cdcq(本博客废弃!现用博客:https://www.cn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ1005HNOI2008明明的烦恼相关的知识,希望对你有一定的参考价值。

又是看黄学长的代码写的,估计我的整个BZOJ平推计划都要看黄学长的代码写

原题:

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

0 < N < = 1000

 

这题用到了树的prufer编码

prufer编码是什么呐

注意下面扯到的树都是无根树(下面不少定义是从黄学长哪里直接粘过来的)

每次删除树中度数为1且序号最小的节点,并在序列中添加与其相邻的节点的序号,直到树中有两个节点,手玩一组小数据很容易理解(逃

呢么任意一棵树都有唯一的长度为n-2的prufer编码,且度数为m的节点在编码中出现了m-1次

呢么就可以将编码还原回一棵树,从prufer编码的最前端开始扫描节点,设该节点序号为 u ,寻找不在prufer编码的最小序号且没有被标记的节点 v ,连接   u,v,并标记v,将u从prufer编码中删除。扫描下一节点。

题中已经把度数给了,呢么就用prufer编码解决

不会写数学表达式,直接粘黄学长的解释吧(sro_hzwer_orz)

题目很丧心病狂的没有让膜一个数,所以要高精度

然而如果用高精度除就亏了,因为这个式子求的是方法数,最后肯定是个整数,呢么就可以分解质因数然后加减,最后再高精度乘

高精度乘可以使用万进制优化,这里有个小技巧,scanf("%abd");表示输出b位数,不够的部分前面补a

因为高精度乘WA了5次,实力会随着时间的推移而减弱qaq

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int mo=100000;
 8 int n,a[1100];
 9 int m=0,tot=0;
10 int zhi[110000],ztop=0;
11 bool kang[510000];
12 int num[110000];
13 int ans[1100000],la=0;
14 void shai(){
15     memset(kang,0,sizeof(kang));
16     for(int i=2;i<=500000;i++)if(!kang[i]){
17         zhi[++ztop]=i;
18         int temp=2;
19         while(i*temp<=500000){
20             kang[i*temp]=true;
21             temp++;
22         }
23     }
24 }
25 void buff(int x,int y){//hzwer_orz,用一个参数可以将加和减的代码合并
26     for(int i=1;i<=x;i++){//阶乘
27         int c=i;
28         for(int j=1;c>=zhi[j];j++)
29             while(!(c%zhi[j])){
30                 num[j]+=y;
31                 c/=zhi[j];
32             }
33     }
34 }
35 void mul(int x){
36     for(int i=1;i<=la;i++)  ans[i]*=x;
37     for(int i=1;i<=la;i++){
38         ans[i+1]+=ans[i]/mo;
39         ans[i]%=mo;
40     }
41     while(ans[la+1]){  la++;  ans[la+1]+=ans[la]/mo;  ans[la]%=mo;}
42 }
43 int main(){
44     //freopen("ddd.in","r",stdin);
45     //freopen("bzoj_1005.in","r",stdin);
46     //freopen("bzoj_1005.out","w",stdout);
47     memset(num,0,sizeof(num));
48     shai();
49     cin>>n;
50     if(n==1){
51         scanf("%d",&a[1]);//注意因为a要--,所以这个特判不能放下面
52         if(!a[1] || a[1]==-1)  cout<<1<<endl;
53         else  cout<<0<<endl;
54         return 0;
55     }
56     for(int i=1;i<=n;i++){
57           scanf("%d",&a[i]);
58         if(!a[i]){  cout<<0<<endl;  return 0;}
59         if(a[i]==-1)  m++;
60         else  a[i]--,tot+=a[i];
61     }
62     buff(n-2,1);
63     buff(n-2-tot,-1);
64     for(int i=1;i<=n;i++)if(a[i])  buff(a[i],-1);
65     ans[la=1]=1;
66     for(int i=1;i<=ztop;i++)
67         while(num[i] --> 0)//趋向于
68             //mul(num[i]);静态差错多重要?这是第二个傻逼错误了
69             mul(zhi[i]);
70     for(int i=1;i<=n-2-tot;i++)
71         mul(m);
72     //for(int i=1;i<=la;i++)  cout<<ans[i]<<" ";  cout<<endl;
73     cout<<ans[la];//之前写成最后输出ans[1]  qaq
74     for(int i=la-1;i>=1;i--)  printf("%05d",ans[i]);//之前写成2到la了,实力会随着时间的推移而降低qaq,然后还是之前写成%04 qaq
75     cout<<endl;
76     //因为高精度乘WA了5次QAQ
77     return 0;
78 }
View Code

 

以上是关于BZOJ1005HNOI2008明明的烦恼的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1005--[HNOI2008]明明的烦恼(树的prufer编码)

bzoj 1005: [HNOI2008]明明的烦恼

bzoj 1005: [HNOI2008]明明的烦恼

bzoj 1005: [HNOI2008]明明的烦恼(组合数学 purfer sequence)

[BZOJ]1005 明明的烦恼(HNOI2008)

[BZOJ]1005 明明的烦恼(HNOI2008)