bzoj1005: [HNOI2008]明明的烦恼(prufer数列+高精度)

3 篇文章 0 订阅

题目传送门
。。。

解法:
卡了一天。。
原来是高精度数组开小了???
其实就是组合嘛。
假设cnt为-1的个数。
sum为各个规定的度数-1的和。

那么首先在一个n-2的prufer数列里面。
有sum个位置被确定了。
首先就是n-2里选sum。
然后sum个可以进行排列。
那么乘sum的阶乘。
但是里面会有重复。
所以需要除以每个(d[i]-1)的阶乘。
这里我用的是分解质因数。跟上一题一样的原理。

最后还剩下n-2-sum个位置,每个位置有cnt种选择。
所以最后乘cnt的(n-2-sum)次方就好。

代码实现:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int d[1100],pr[1100],cnt[1100];
bool pd(int x) {
    if(x<=1)return false;
    int t=int(sqrt(double(x+1)));
    for(int i=2;i<=t;i++)if(x%i==0)return false;
    return true;
}
struct node {int len,a[11000];node() {len=0;memset(a,0,sizeof(a));}};
node chengfa(node n1,node n2) {
    node no;no.len=n1.len+n2.len-1;
    for(int i=1;i<=n1.len;i++)for(int j=1;j<=n2.len;j++)no.a[i+j-1]+=n1.a[i]*n2.a[j];
    for(int i=1;i<=no.len;i++) {no.a[i+1]+=no.a[i]/10;no.a[i]%=10;}
    int i=no.len;while(no.a[i+1]>0) {i++;no.a[i+1]+=no.a[i]/10;no.a[i]%=10;}no.len=i;
    return no;
}
node turn(int x) {node no;while(x!=0) {no.len++;no.a[no.len]=x%10;x/=10;}return no;}
node pow_mod(node a,int b) {
    node ans;ans.len=1;ans.a[1]=1;
    while(b!=0) {if(b%2==1)ans=chengfa(ans,a);a=chengfa(a,a);b/=2;}
    return ans;
}
int main() {
    //freopen("tree2.in","r",stdin);
    int n,s=0,sum=0;scanf("%d",&n);
    for(int i=1;i<=n;i++) {scanf("%d",&d[i]);if(d[i]==-1)s++;else sum+=d[i]-1;}
    if(sum>n-2) {printf("0\n");return 0;}
    if(sum<n-2&&s==0) {printf("0\n");return 0;}
    if(n==1&&d[1]>=1) {printf("0\n");return 0;}
    if(n==1) {printf("1\n");return 0;}
    for(int i=1;i<=n;i++)if(d[i]==0&&n!=1) {printf("0\n");return 0;}
    if(n==2){
        if((d[1]==0||d[1]>1)||(d[2]==0||d[2]>1)) printf("0\n");
        else printf("1\n");
        return 0;
    }
    int len=0;for(int i=1;i<=n;i++)if(pd(i)==true)pr[++len]=i;
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=sum;i++) {
        int x=(n-2)-i+1;
        for(int j=1;j<=len;j++) {
            if(pr[j]>x)break;
            while(x%pr[j]==0&&x!=0) {cnt[j]++;x/=pr[j];}
        }
    }
    for(int i=1;i<=n;i++) {
        if(d[i]==-1)continue;
        for(int k=2;k<=d[i]-1;k++) {
            int x=k;
            for(int j=1;j<=len;j++) {
                if(pr[j]>x)break;
                while(x%pr[j]==0&&x!=0) {cnt[j]--;x/=pr[j];}
            }
        }
    }
    node ans;ans.len=1;ans.a[1]=1;
    for(int i=1;i<=len;i++)ans=chengfa(ans,pow_mod(turn(pr[i]),cnt[i]));
    node t=turn(s);ans=chengfa(ans,pow_mod(t,n-2-sum));
    for(int i=ans.len;i>=1;i--)printf("%d",ans.a[i]);printf("\n"); 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值