bzoj 1005: [HNOI2008]明明的烦恼 树的prufer序列+万进制

题目传送门

思路:

  这道题需要前置知识prufer编码,这篇博客对prufer编码和这道题的分析写的很好。

  这里主要讲一些对大数阶乘的分解,一个办法当然是用高精度,上面这篇博客用的是java,还有一个办法是用万进制,但是普通的万进制只能计算乘法,而这里需要用到除法,又不能用逆元(因为没有取模)怎么办呢?

  我们发现,上面那篇博客得到的式子是一个组合数的式子,所以必然是整数,如果把分子和分母共同进行质因子分解,那么上面的质因子的数量必然大于下面的,所以我们就把每一个阶乘和数字进行质因子分解,然后对分解出来的质因子用万进制处理(我实际上用的是百万进制)。

  代码debug的时候有个很小的地方错了,看了一遍hzwer聚聚的代码,,然后就变默写了。。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
#define fpn() freopen("simple.in","r",stdin)
#define rd read()
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
const int maxn=1010;
int p=1000000;
int ans[maxn],num[maxn],pri[maxn],cnt,l,tot;
int d[maxn],n,sum;
inline bool judge(int x){
    for(int i=2;i<=sqrt(x);i++){
        if(x%i==0)return false;
    }
    return true;
}
void prim(){
    for(int i=2;i<=1000;i++)
    {
        if(judge(i))pri[++cnt]=i;
    }
}
void resolve(int x,int w){
    for(int k=2;k<=x;k++)
    {
        int a=k;
        for(int i=1;i<=cnt;i++){
            if(a<=1)break;
            while(a%pri[i]==0){
                num[i]+=w;
                a/=pri[i];
            }
        }
    }
}
void mul(int x){
    for(int i=1;i<=l;i++)ans[i]*=x;
    for(int i=1;i<=l;i++){
        ans[i+1]+=ans[i]/p;
        ans[i]%=p;
    }
    while(ans[l+1]>0){
        l++;
        ans[l+1]+=ans[l]/p,ans[l]%=p;
    }
}
void print()
{
    for(int i=l;i>0;i--)
        if(i==l)printf("%d",ans[i]);
        else printf("%06d",ans[i]);
}
int main(){
    prim();
    cin>>n;
    if(n==1){
        int x;
        cin>>x;
        if(!x)printf("1\n");
        else puts("0");
        return 0;
    }
    int flag=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&d[i]);
        if(d[i]!=-1){
            if(d[i]==0)flag=1;
            tot++;
            sum+=d[i]-1;
        }
    }
    if(sum>n-2||flag){
        puts("0");
        return 0;
    }
    resolve(n-2,1);
    resolve(n-2-sum,-1);
    for(int i=1;i<=n;i++){
        if(d[i]!=-1){
            resolve(d[i]-1,-1);
        }
    }
    ans[++l]=1;
    for(int i=1;i<=cnt;i++){
        while(num[i]--){
            mul(pri[i]);
        }
    }
    for(int i=1;i<=n-2-sum;i++){
        mul(n-tot);
    }
    print();
    return 0;
}
View Code

 

posted @ 2019-02-25 20:35  光芒万丈小太阳  阅读(193)  评论(0编辑  收藏  举报