首页 > 代码库 > 【BZOJ1005】【HNOI2008】明明的烦恼

【BZOJ1005】【HNOI2008】明明的烦恼

又是看黄学长的代码写的,估计我的整个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]  qaq74     for(int i=la-1;i>=1;i--)  printf("%05d",ans[i]);//之前写成2到la了,实力会随着时间的推移而降低qaq,然后还是之前写成%04 qaq75     cout<<endl;76     //因为高精度乘WA了5次QAQ77     return 0;78 }
View Code

 

【BZOJ1005】【HNOI2008】明明的烦恼