zoukankan      html  css  js  c++  java
  • BZOJ1005明明的烦恼 Prufer + 分解質因數 + 高精度

    @[高精度, Prufer, 質因數分解]

    Description

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

    Input

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

    Output

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

    Sample Input

    3
    1
    -1
    -1
    

    Sample Output

    2
    

    HINT

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

    Solution

    Prufer編碼的簡單應用.
    Prufer是無根樹的一種編碼, 可以和無根樹之間形成一一對應關係. 生成Prufer編碼的方法很簡單, 大致過程如下(這都不是重點)

    一种生成Prufer序列的方法是迭代删点,直到原图仅剩两个点。对于一棵顶点已经经过编号的树T,顶点的编号为{1,2,...,n},在第i步时,移去所有叶子节点(度为1的顶点)中标号最小的顶点和相连的边,并把与它相邻的点的编号加入Prufer序列中,重复以上步骤直到原图仅剩2个顶点。以右边的树为例子,首先在所有叶子节点中编号最小的点是2,和它相邻的点的编号是3,将3加入序列并删除编号为2的点。接下来删除的点是4,5被加入序列,然后删除5,1,此时原图仅剩两个点,Prufer序列构建完成,为{3,5,1,3}
    enter image description here

    重點在於: 在Prufer編碼中, $$一個節點出現的次數 = 該點的度 - 1$$
    有了這個性質, 這題就可以簡單地轉化為求Prufer編碼的排列組合數量.

    【思考】怎么计算呢?就用排列组合的原理。我们先考虑不是-1的点。一共有N-2个格子,设当前的点要求为a1(已经减1了),我们可以放的总数是:C(N-2,a1)。这样占了a1个格子。下一个a2的情况就是C(N-2-a1,a2)。依次类推。
    然后是-1的情况,我想了很长时间。
    原来是这样想的:设最后还有K个格子,且有SUM个-1。我们用DP思路。f[i][j]表示放到最后K个格子中的第i个格子时,放第j种颜色(注意颜色是不下降的,最后只需乘上阶乘即可)的情况数。但是因为要用高精,DP会很麻烦。
    感谢HHD大牛的指导:其实就是SUM^K。为什么?对于每一个格子,我们都可以放SUM种情况~~~~。
    【优化】为了不超时,用质数表来优化。

    實際上就是用分解質因數的方法來優化, 然後再用高精度計算最終答案.

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    using namespace std;
    inline int read()
    {
    	int x = 0, flag = 1;
    	char c;
    	while(! isdigit(c = getchar()))
    		if(c == '-')
    			flag *= - 1;
    	while(isdigit(c))
    		x = x * 10 + c - '0', c = getchar();
    	return x * flag;
    }
    void println(int x)
    {
        if(x < 0)
            putchar('-'), x *= - 1;
        if(x == 0)
            putchar('0');
        int ans[10], top = 0;
        while(x)
            ans[top ++] = x % 10, x /= 10;
        for(; top; top --)
            putchar(ans[top - 1] + '0');
        putchar('
    ');
    }
    const int N = 1 << 10;
    int top;
    int isprime[N], prime[N];
    void get_prime()
    {
    	memset(isprime, - 1, sizeof(isprime));
    	top = 0;
    	for(int i = 2; i < N; i ++)
    		if(isprime[i] == - 1)
    		{
    			isprime[i] = 1, prime[top ++] = i;
    			for(int j = i << 1; j < N; j += i)
    				isprime[j] = 0;
    		}
    }
    int ans[N];
    void C(int n, int m)
    {
    	for(int i = n; i > n - m; i --)
    	{
    		int p = i;
    		for(int j = 0; j < top; j ++)
    			while(! (p % prime[j]))
    				ans[j] ++, p /= prime[j];
    	}
    	for(int i = m; i; i --)
    	{
    		int p = i;
    		for(int j = 0; j < top; j ++)
    			while(! (p % prime[j]))
    				ans[j] --, p /= prime[j];
    	}
    }
    struct Giant
    {
    	int dig[1 << 20];
    	int top;
    	Giant()
    	{
    		top = 1;
    		memset(dig, 0, sizeof(dig));
    		dig[0] = 1;
    	}
    };
    void operator *(Giant &x, int y)
    {
    	for(int i = 0; i < x.top; i ++)
    		x.dig[i] *= y;
    	for(int i = 0; i < x.top; i ++)
    		if(x.dig[i] > 9)
    			x.dig[i + 1] += x.dig[i] / 10, x.dig[i] %= 10;
    	while(x.dig[x.top])
    		x.dig[x.top + 1] = x.dig[x.top] / 10,
    		x.dig[x.top] %= 10,
    		x.top ++;
    }
    void println(Giant &x)
    {
    	for(int i = x.top; i; i --)
    		putchar(x.dig[i - 1] + '0');
    	putchar('
    ');
    }
    Giant Ans; 
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("BZOJ1005.in", "r", stdin);
    	freopen("BZOJ1005.out", "w", stdout);
    	#endif
    	get_prime();
    	int n = read();
    	int len = 0, cnt = 0;	
    	//len記錄prufer序列已經佔用的長度, cnt記錄尚未確定度的點數 
    	for(int i = 0; i < n; i ++)
    	{
    		int D = read();
    		if((! D) || (D - 1 + len > n - 2))
    		{
    			println(0);
    			return 0;
    		}
    		if(D == - 1)
    			cnt ++;
    		if(D > 0)
    			C(n - 2 - len, D - 1), len += D - 1;
    	}
    	if(len < n - 2)
    	{
    		for(int i = 0; i < top; i ++)
    			while(! (cnt % prime[i]))
    				ans[i] += n - 2 - len, cnt /= prime[i];
    	}
    	for(int i = 0; i < top; i ++)
    		for(int j = 0; j < ans[i]; j ++)
    			Ans * prime[i];
    	println(Ans);
    }
    
  • 相关阅读:
    超经典~超全的jQuery插件大全
    如何用PHP做到页面注册审核
    php实现签到功能
    php中的实用分页类
    微信小程序,超能装的实例教程
    php之 常用的 流程管理
    php之 人员的权限管理(RBAC)
    php之简单的文件管理(基本功能)
    php最新学习-----文件的操作
    关于LAMP的配置之(虚拟机的安装、创建、配置)
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/6483041.html
Copyright © 2011-2022 走看看