zoukankan      html  css  js  c++  java
  • bzoj 1005: [HNOI2008]明明的烦恼(Prufer序列,组合数学,关于n!素数分解的一个定理)

    题目链接

    1005: [HNOI2008]明明的烦恼

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 4772  Solved: 1865
    [Submit][Status][Discuss]

    Description

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

    Input

      第一行为N(0 < N < = 1000),
    接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1

    Output

      一个整数,表示不同的满足要求的树的个数,无解输出0

    Sample Input

    3
    1
    -1
    -1

    Sample Output

    2

    HINT

      两棵树分别为1-2-3;1-3-2

    Source

     
    [Submit][Status][Discuss]

    首先,说一说树的prufer序列:

    Prufer序列

    把一棵树进行以下操作:

    1.找到编号最小的叶节点,删除这个节点,然后与这个叶节点相连的点计入序列

    2.反复进行1,直到这棵树只剩下两个节点时,退出

    比如说这个图(来自度受百科)

    最小叶节点为2,删除2,将3计入序列

    最小叶节点为4,删除4,将5计入序列

    最小叶节点为5,删除5,将1计入序列

    最小叶节点为1,删除1,将3计入序列

    图中只剩下两个节点,退出

    于是得到这棵树的Prufer序列为{3,5,1,3}

    这样可以得到一个长度为n-2的序列。很容易证明,树和Prufer序列是一一对应的

    Prufer序列显然满足一个性质:一个点若度数为d,则一定在Prufer序列中出现了d-1次

    除此之外,还有一个重要的定理:

    黄学长博客

    一下是博客题解内容:

    该题运用到了树的prufer编码的性质:
      (1)树的prufer编码的实现
            不断 删除树中度数为1的最小序号的点,并输出与其相连的节点的序号  直至树中只有两个节点
      (2)通过观察我们可以发现
            任意一棵n节点的树都可唯一的用长度为n-2的prufer编码表示
            度数为m的节点的序号在prufer编码中出现的次数为m-1
      (3)怎样将prufer编码还原为一棵树??
            从prufer编码的最前端开始扫描节点,设该节点序号为 u ,寻找不在prufer编码的最小序号且没有被标记的节点 v ,连接   u,v,并标记v,将u从prufer编码中删除。扫描下一节点。
     
     
     
     
    该题需要将树转化为prufer编码:
     n为树的节点数,d[ ]为各节点的度数,m为无限制度数的节点数。
    则            
    所以要求在n-2大小的数组中插入tot各序号,共有种插法;
    在tot各序号排列中,插第一个节点的方法有种插法;
                               插第二个节点的方法有种插法;
                                          ………
    另外还有m各节点无度数限制,所以它们可任意排列在剩余的n-2-tot的空间中,排列方法总数为
     
    根据乘法原理:
     
     
    然后就要高精度了…..但高精度除法太麻烦了,显而易见的排列组合一定是整数,所以可以进行质因数分解,再做一下相加减。
     
     
    关于n!质因数分解有两种方法,第一种暴力分解,这里着重讲第二种。
      若p为质数,则n!可分解为 一个数*,其中  <n(此处应为<=)
     
    所以 

    ——转自怡红公子

    由于这题要大整数,我懒得写,于是写了个这一题的削弱版本:bzoj1211:链接

    1211: [HNOI2004]树的计数

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 2347  Solved: 824
    [Submit][Status][Discuss]

    Description

    一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵。给定n,d1, d2, …, dn,编程需要输出满足d(vi)=di的树的个数。

    Input

    第一行是一个正整数n,表示树有n个结点。第二行有n个数,第i个数表示di,即树的第i个结点的度数。其中1<=n<=150,输入数据保证满足条件的树不超过10^17个。

    Output

    输出满足条件的树有多少棵。

    Sample Input

    4
    2 1 2 1

    Sample Output

    2

    HINT

     

    Source

     
    [Submit][Status][Discuss]
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<stack>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define pb push_back
    #define fi first
    #define se second
    typedef vector<int> VI;
    typedef long long ll;
    typedef pair<int,int> PII;
    const int inf=0x3fffffff;
    const ll mod=1000000007;
    const int maxn=200+10;
    int prime[maxn],deg[maxn];
    ll fp(int i,int y)
    {
        ll ans=1,t=i;
        while(y)
        {
            if(y&1) ans=ans*t;
            t=t*t;
            y/=2;
        }
        return ans;
    }
    void getPrim()
    {
        for(int i=2;i<maxn;i++)
        {
            if(!prime[i])
            {
                prime[++prime[0]] = i;
            }
            for(int j=1;(j<=prime[0])&&(i*prime[j]<maxn);j++)
            {
                prime[prime[j]*i] = 1;
                if(i%prime[j]==0) break;
            }
        }
    }
    int p1[maxn],p2[maxn];
    int p[maxn];
    void get(int x)
    {
        for(int i=1;prime[i]<=x&&i<=prime[0];i++)
        {
            int t=prime[i];
            int cnt=0;
            while(t<=x) //
            {
                cnt+=x/t;
                t=t*prime[i];
            }
            p2[prime[i]]+=cnt;
        }
    }
    int main()
    {
        int n,tot=0;
        getPrim();
        scanf("%d",&n);
        if(n==1)
        {
            int u;
            scanf("%d",&u);
            if(u!=0) puts("0");
            else puts("1");
            return 0;
        }
        rep(i,1,n+1)
        {
            scanf("%d",&deg[i]);
            if(deg[i]<=0)
            {
                puts("0");return 0;
            }
            tot+=deg[i]-1;
        }
        if(tot!=n-2){puts("0");return 0;}
        for(int i=1;prime[i]<=n-2&&i<=prime[0];i++)
        {
            int t=prime[i];
            int cnt=0;
            while(t<=n-2) //
            {
                cnt+=(n-2)/t;
                t=t*prime[i];
            }
            p1[prime[i]]+=cnt;
        }
        rep(i,1,n+1) get(deg[i]-1);
        ll ans=1;
        rep(i,1,prime[0]+1)
        {
            int cnt=p1[prime[i]]-p2[prime[i]];
            if(cnt)
            {
                ans*=fp(prime[i],cnt);
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    编程算法
    2048小游戏主要算法实现
    Spring 计时器 @Scheduled cron 含义
    jQuery Validate Ajax 验证
    jQuery Validate 使用
    Java 两个整数相除保留两位小数,将小数转化为百分数
    mybatis+mysql返回插入的主键,参数只是提供部分参数
    微信 创建自定义菜单 向微信发起的post请求
    JSON.parse()和JSON.stringify()
    前台JSON字符串,spring mvc controller也接收字符串
  • 原文地址:https://www.cnblogs.com/tarjan/p/6613108.html
Copyright © 2011-2022 走看看