HNOI2008明明的烦恼

其实挺难的,对prufer得有一定的理解。

首先我们知道prufer序列最多有n-2个点,其余知道度数的点出现的次数是di-1,定义prufer序列中剩余出现次数sum=n-2-∑(di-1)。这样我们可以近似于我们已经知道所有点的出现次数,用prufer的公式$frac{(n-2)!}{prod_{i=1}^n(d_i-1)!sum!}$,其中di是已知的度数,不一定由1到n,那个公式是搬过来的。

这样的话我们忽略了一个问题,我们只是近似地认为所有点出现的次数已知,其实sum那一部分还有很多情况被忽略,因为我们并不知道剩下的位置到底是谁,那么,如果还剩下m个点不知道度数,那么在sum个位置任意填上他们的方案数为$m^{sum}$。把这两部分乘到一起就是答案。

还有一个问题,这个东西是要高精的,这一坨坨的阶乘非常适合直接用阶乘拆分(至于如果你不知道什么是阶乘拆分,请看这个小链接),然后用qpow乘到一起,只是有点懒,直接for往上乘的也可以,没有qpow,也没有亿进制优化。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,du[2000];
int prime[1000],prime_num,sum,m;
bool v[2000];
struct Bigint{
    int a[100000],len;
    void clear(){
        memset(a,0,sizeof(a));
        a[1]=1;
        len=1;
    }
    friend void operator *(Bigint &x,int y){
        int delta=0;
        for(int i=1;i<=x.len;i++){
            x.a[i]=x.a[i]*y+delta;
            delta=x.a[i]/10;
            x.a[i]%=10;
        }
        while(delta){
            x.a[++x.len]=delta%10;
            delta/=10;
        }
        while(x.a[x.len]==0&&x.len>1)
            x.len--;
    }
    void out(){
        for(int i=len;i>=1;i--)
            printf("%lld",a[i]);
    }
}ans;
int read(){
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){
        if(x=='-') f=-1;
        x=getchar();
    }while(x>='0'&&x<='9'){
        sum=sum*10+x-'0';
        x=getchar();
    }return sum*f;
}
void doprime(int x){
    for(int i=2;i<=x;i++){
        if(!v[i]) prime[++prime_num]=i;
        for(int j=1;j<=prime_num&&i*prime[j]<=x;j++){
            v[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}
signed main(){
    //freopen("data.in","r",stdin);
    //freopen("2.out","w",stdout);
    n=read();
    for(int i=1,x;i<=n;i++){
        x=read();
    /*    if(!x){
            puts("0");
            return 0;
        }*/
        if(x==-1) continue;
        du[++du[0]]=x;
        sum+=du[du[0]]-1;
        m++;
        if(n-2<sum){
            puts("0");
            return 0;
        }
    }
    sum=n-2-sum;
    m=n-m;
//    cout<<m<<" "<<sum<<endl;
    doprime(n);ans.clear();
/*    for(int i=1;i<=prime_num;i++)
        cout<<prime[i]<<" ";cout<<endl;*/
    for(int i=1;i<=prime_num;i++){
        int s=0;
        for(int j=n-2;j/=prime[i];) s+=j;
        //cout<<"s1="<<s<<endl;
        for(int j=1;j<=du[0];j++)
            for(int k=du[j]-1;k/=prime[i];) s-=k;
        for(int j=sum;j/=prime[i];) s-=j;
        for(int j=1;j<=s;j++)
            ans*prime[i];
    }
    for(int i=1;i<=sum;i++)
        ans*m;
    ans.out();
    puts("");
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Yu-shi/p/11235529.html