bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数

1005: [HNOI2008]明明的烦恼

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 2248  Solved: 898
[Submit][Status]

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

生成树prufer标号的性质(觉得相当神奇啊):

1.prufer序列和树一一对应。

2.序列的长度=树的节点个数-2。

3.树中该节点的度数=该节点编号在序列中出现的次数+1,叶子节点不会出现在序列中。

每一个标号有且仅有一个对应的生成树。

有些时候当计数问题不含加减,但含除法时,可通过分解质因数简化。

而且光靠想出来的公式很不靠谱的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1010
#define MAXL 1000
#define VAL1 10000
class number//四位
{
        public:
                number()
                {
                        clear();
                }
                bool is_odd()
                {
                        return numb[0]%2==1;
                }
                bool is_even()
                {
                        return numb[0]%2==0;
                }
                void lsh_bin()
                {
                        int i;
                        for (i=topn;i>0;i--)
                        {
                                if (numb[i]%2==1)
                                {
                                        numb[i-1]+=VAL1;
                                }
                                numb[i]/=2;
                        }
                        numb[0]/=2;
                        while (topn&&!numb[topn])topn--;
                }
                bool equal_to(int x)
                {
                        if (topn==0)
                        {
                                return x==numb[0];
                        }
                        if (topn==1)
                        {
                                return x==numb[0]+numb[1]*VAL1;
                        }
                        return false;
                }
                int size()
                {
                        return topn;
                }
                int length()
                {
                        int x=numb[topn];
                        int ret=0;
                        while (x)
                        {
                                ret++;
                                x/=10;
                        }
                        int y=0;
                        x=VAL1;
                        while (x)
                        {
                                y++;
                                x/=10;
                        }
                        y--;
                        ret+=topn*y;
                        return ret;
                }
                void operator =(int x)//{{{
                {
                        int now=0;
                        clear();
                        numb[now]=x;
                        while (numb[now]>=VAL1)
                        {
                                numb[now+1]+=numb[now]/VAL1;
                                numb[now]%=VAL1;
                                now++;
                                if (now>topn)topn=now;
                        }
                }//}}}
void operator =(number num)//{{{
{
        topn=num.topn;
        memcpy((this->numb),num.numb,sizeof(num.numb[0])*(topn+1));
}//}}}
void operator +=(number &num)//{{{
{
        int i;
        topn=max(topn,num.topn);
        for (i=0;i<=topn;i++)
        {
                numb[i]+=num.numb[i];;
                if (numb[i]>=VAL1)
                {
                        numb[i+1]+=numb[i]/VAL1;
                        numb[i]%=VAL1;
                }
        }
        while (numb[topn+1])
        {
                topn++;
                numb[topn+1]+=numb[topn]/VAL1;
                numb[topn]%=VAL1;
        }
}//}}}
void operator +=(int x)//{{{
{
        int now=0;
        if (topn==-1)topn=0;
        numb[now]+=x;
        while (numb[now]>=VAL1)
        {
                numb[now+1]+=numb[now]/VAL1;
                numb[now]%=VAL1;
                now++;
                if (now>topn)topn=now;
        }
}//}}}
void operator *=(int x)//{{{
{
        int i;
        for (i=0;i<=topn;i++)
        {
                numb[i]*=x;
        }
        for (i=0;i<=topn;i++)
        {
                if (numb[i]>=VAL1)
                {
                        numb[i+1]+=numb[i]/VAL1;
                        numb[i]%=VAL1;
                }
        }
        while (numb[topn+1])
        {
                topn++;
                numb[topn+1]+=numb[topn]/VAL1;
                numb[topn]%=VAL1;
        }
}//}}}
void operator *=(number &num)
{
        number ret;
        ret=(*this)*num;
        (*this)=ret;
}
void operator -=(number &num)//{{{
{
        if (*this<num)throw "Error!\n->void operator -=(number &num)\n";
        int i;
        for (i=0;i<=topn;i++)
        {
                numb[i]-=num.numb[i];
        }
        for (i=0;i<=topn;i++)
        {
                while (numb[i]<0)
                {
                        numb[i]+=VAL1;
                        numb[i+1]--;
                }
        }
        while (topn&&!numb[topn])topn--;
}//}}}
void operator /=(int x)
{
        int i;
        int tot=0;
        for (i=topn;i>=0;i--)
        {
                tot=tot*VAL1+numb[i];
                numb[i]=tot/x;
                tot%=x;
        }
        while (topn&&!numb[topn])topn--;
}
void operator --(int)//{{{
{
        if (topn==0&&numb[0]==0)throw "Error!\n->void operator --(int)\n";
        int now=0;
        numb[now]--;
        while (numb[now]<0)
        {
                numb[now+1]--;
                numb[now]+=VAL1;
        }
        while (topn&&!numb[topn])topn--;
}//}}}
private:
int numb[MAXL];
int topn;
void clear()
{
        topn=0;
        memset(numb,0,sizeof(numb));

}
friend bool operator <(number num1,number num2);
friend bool operator <=(number num1,number num2);
friend bool operator ==(number num1,number num2);
friend ostream& operator <<(ostream &out,number &num);
friend istream& operator >>(istream &in,number &num);
friend number operator *(number &num1,number &num2);
friend number operator *(number num,int x);
friend number operator +(number num1,number num2);
friend number operator +(number num1,int x);
friend number operator -(number num1,number num2);
//a=a+b远没有a+=b快
};
bool operator <(number num1,number num2)//{{{
{
        if (num1.topn!=num2.topn)
        {
                return num1.topn<num2.topn;
        }
        int i;
        for (i=num1.topn;i>=0;i--)
        {
                if (num1.numb[i]!=num2.numb[i])
                {
                        return num1.numb[i]<num2.numb[i];
                }
        }
        return false;
}//}}}
bool operator <=(number num1,number num2)//{{{
{
        if (num1.topn!=num2.topn)
        {
                return num1.topn<num2.topn;
        }
        int i;
        for (i=num1.topn;i>=0;i--)
        {
                if (num1.numb[i]!=num2.numb[i])
                {
                        return num1.numb[i]<num2.numb[i];
                }
        }
        return true;
}//}}}
bool operator ==(number num1,number num2)//{{{
{
        if (num1.topn!=num2.topn)return false;
        for (int i=0;i<=num1.topn;i++)
        {
                if (num1.numb[i]!=num2.numb[i])return false;
        }
        return true;
}//}}}
ostream& operator <<(ostream &out,number &num)//{{{
{
        int i;
        out<<num.numb[num.topn];
        for (i=num.topn-1;i>=0;i--)
        {
                //压六位时
                //        if (num.numb[i]<100000)out<<"0";
                //        if (num.numb[i]<10000)out<<"0";
                if (num.numb[i]<1000)out<<"0";
                if (num.numb[i]<100)out<<"0";
                if (num.numb[i]<10)out<<"0";
                out<<num.numb[i];
        }
        return out;
}//}}}
istream& operator >>(istream &in,number &num)//{{{
{
        string str;
        in>>str;
        int i;
        num.clear();
        for (i=(int)str.length()-1,num.topn=0;i>=0;i-=4,num.topn++)
        {
                if (i-3<str.length())
                {
                        num.numb[num.topn]=(str[i]-‘0‘)+10*(str[i-1]-‘0‘)+100*(str[i-2]-‘0‘)+1000*(str[i-3]-‘0‘);
                }else
                {
                        if (i-2<str.length())num.numb[num.topn]+=100*(str[i-2]-‘0‘);
                        if (i-1<str.length())num.numb[num.topn]+=10*(str[i-1]-‘0‘);
                        if (i  <str.length())num.numb[num.topn]+=(str[i]-‘0‘);
                }
        }
        num.topn--;
        return in;
}//}}}
number operator *(number num,int x)//{{{
{
        number ret;
        ret=num;
        ret*=x;
        return ret;
}//}}}
number operator * (number &num1,number &num2)
{
        int i,j;
        number ret;
        ret.topn=num1.topn+num2.topn;
        for (i=0;i<=num1.topn;i++)
        {
                for (j=0;j<=num2.topn;j++)
                {
                        ret.numb[i+j]+=num1.numb[i]*num2.numb[j];
                        if (ret.numb[i+j]>=VAL1)
                        {
                                ret.numb[i+j+1]+=ret.numb[i+j]/VAL1;
                                ret.numb[i+j]%=VAL1;
                        }
                }
        }
        for (i=0;i<=ret.topn;i++)
        {
                if (ret.numb[i]>=VAL1)
                {
                        ret.numb[i+1]+=ret.numb[i]/VAL1;
                        ret.numb[i]%=VAL1;
                }
        }
        while (ret.numb[ret.topn+1])
        {
                ret.topn++;
                ret.numb[ret.topn+1]+=ret.numb[ret.topn]/VAL1;
                ret.numb[ret.topn]%=VAL1;
        }
        return ret;
}
number operator +(number num1,number num2)//{{{
{
        number ret;
        ret=num1;
        ret+=num2;
        return ret;
}//}}}
number operator +(number num1,int x)//{{{
{
        number ret;
        ret=num1;
        ret+=x;
        return ret;
}//}}}
number operator -(number num1,number num2)//{{{
{
        number ret;
        ret=num1;
        ret-=num2;
        return ret;
}//}}}
number c (int x,int y)
{
        number ret;
        ret=1;
        int i;
        for (i=y+1;i<=x;i++)
                ret*=i;
        for (i=1;i<=(x-y);i++)
                ret/=i;
        return ret;
}
int main()
{
        freopen("input.txt","r",stdin);
        int x;
        int n;
        scanf("%d",&n);
        number ans;
        number temp1,temp2,temp3;
//        number temp;
//        temp=c(1000,2);
//        cout<<temp<<endl;
        ans=1;
        int r=n-2;
        int i,j;
        j=1;
        int totu=0;
        for (i=0;i<n;i++)
        {
                scanf("%d",&x);
                if (x==0)
                {
                        printf("0\n");
                        return 0;
                }
                if (x!=-1)
                {
                        if (!r&&x-1)
                        {
                                printf("0\n");
                                return 0;
                        }
                        temp1=c(r,x-1);
                        ans*=temp1;
                        r-=x-1;
                        if (r<0)
                        {
                                printf("0\n");
                                return 0;
                        }
                }else
                {
                        totu++;
                }
        }
        if (r&&totu<=0)
        {
                printf("0\n");
                return 0;
        }
        if (r)
        {
                for (i=1;i<=r;i++)
                {
                        ans=ans*totu;
                }
        }
        cout<<ans<<endl;
}

bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数

时间: 2024-10-13 11:19:43

bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数的相关文章

bzoj 1005: [HNOI2008]明明的烦恼(组合数学 purfer sequence)

1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3945  Solved: 1563 [Submit][Status][Discuss] Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在 任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000), 接下来N行,第i+1行给出第i

BZOJ 1005: [HNOI2008]明明的烦恼 Purfer数列

Purfer数列: http://www.cnblogs.com/zhj5chengfeng/p/3278557.html 题目大意 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为 1 到 N 的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为 N(0<N<=1000),接下来 N 行,第 i+1 行给出第 i 个节点的度数 Di,如果对度数不要求,则输入 -1 Output 一个整数,表示不同的满足要求的树的个数,无解

BZOJ 1005: [HNOI2008]明明的烦恼 Purfer序列 大数

1005: [HNOI2008]明明的烦恼 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1005 Description 自从明明学了树的结构,就对奇怪的树产生了兴趣...... 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行

bzoj 1005: [HNOI2008]明明的烦恼

1 #include<cstdio> 2 #include<iostream> 3 #define M 10005 4 int a[M],n,d[M],f[M],b[M],tot,m; 5 void jia(int a1) 6 { 7 for(int i=1;i<=d[0];i++) 8 for(;a1%d[i]==0;) 9 { 10 b[i]++; 11 a1/=d[i]; 12 } 13 return; 14 } 15 void jian(int a1) 16 { 17

【组合数学】【高精度】【prufer数列】【HNOI 2008】【bzoj 1005】明明的烦恼

1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec Memory Limit: 162 MB Submit: 3217 Solved: 1290 Description 自从明明学了树的结构,就对奇怪的树产生了兴趣-- 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1 Output 一个

【BZOJ1005/1211】[HNOI2008]明明的烦恼/[HNOI2004]树的计数 Prufer序列+高精度

[BZOJ1005][HNOI2008]明明的烦恼 Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1 Output 一个整数,表示不同的满足要求的树的个数,无解输出0 Sample Input 3 1 -1 -1 Sample Outp

【BZOJ】1005: [HNOI2008]明明的烦恼(prufer编码+特殊的技巧)

http://www.lydsy.com/JudgeOnline/problem.php?id=1005 这里讲得挺清楚的:http://www.cnblogs.com/zhj5chengfeng/p/3278557.html 对于有n个节点的树: prufer数列和一棵树一一对应. prufer数列有n-2个元素 prufer数列的构造与解析: 构造:n-2步,每一步在树中查找度为1的点,并将这个点所在的边的另一个点放到prufer数列中,两个点的度均-1. 解析:将所有点的度赋值为1,然后将

【bzoj1005】[HNOI2008]明明的烦恼 Prufer序列+高精度

题目描述 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? 输入 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1 输出 一个整数,表示不同的满足要求的树的个数,无解输出0 样例输入 3 1 -1 -1 样例输出 2 题解 Prufer序列+高精度 Prufer序列:由一棵 $n$ 个点的树唯一产生的一个 $n-2$ 个数的序列. 生成方法:找到这棵树编号最小的叶子节点,将其

[HNOI2008]明明的烦恼

1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5090  Solved: 1986[Submit][Status][Discuss] Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i个节点的