zoukankan      html  css  js  c++  java
  • 【JZOJ5068】【GDSOI2017第二轮模拟】树 动态规划+prufer序列

    题面

    有n个点,它们从1到n进行标号,第i个点的限制为度数不能超过A[i].
    现在对于每个s (1 <= s <= n),问从这n个点中选出一些点组成大小为s的有标号无根树的方案数。
    100%的数据:n <= 100

    100

    prufer序列

    每个大小为n,有标号无根树都可以表示成一个长度为(n-2)且取值在[1,n]的序列。
    

    这个序列就叫prufer序列

    树转prufer序列

    1.每次查找一个标号最小且度数为一的点,使与之相连的点的编号加入序列尾;
    2.删除树中的这个点。

    prufer序列转树

    1.新建一个包含[1,n]的集合;
    2.找出集合中在序列中未出现的最小编号,并使之与序列头连一条边;
    3.删除集合中的这个编号,以及序列头;
    4.重复2-3步骤n-2次,序列则为空;
    5.把集合中的剩余两个编号相连,原树复原。

    性质

    1.长度为(n-2)的prufer序列的集合与大小为n的带标号无根树的集合一一映射。
    2.带标号无根树的任意点的度数等于,与之对应的prufer序列的对应编号出现次数+1。


    Back to Problem

    Pre

    假设我们求出了一个prufer序列的长度(n),并且知道其中每个编号出现的次数(c_i)
    由性质2,那么这种prufer序列的贡献就是,它的不同全排列数,有:

    [Ans=n!*sum_{i=1}^nfrac{1}{c_i!} ]

    正文

    所以我们把原题转化为在序列上的动态规划
    (f_{i,j,k})表示前i个点,用了j个点,序列长度为k的方案数。
    那么就有,

    [f_{i,j,k} Rightarrow egin{cases} f_{i+1,j,k}\ f_{i+1,j+1,k+l},&l in [0,a_i-1] end{cases} ]

    最终答案就是(f_{n,s,s-2}*(s-2)!)
    时间复杂度为(O(n^4)),但由于冗余状态的存在,是能卡过的。
    原题是要用FFT优化到(O(n^3*log_n))

    Code

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define fo(i,x,y) for(int i=x;i<=y;i++)
    #define fd(i,x,y) for(int i=x;i>=y;i--)
    using namespace std;
    const char* fin="tree.in";
    const char* fout="tree.out";
    const int inf=0x7fffffff;
    const int maxn=107,mo=1004535809;
    int n,a[maxn],f[maxn][maxn][maxn],fact[maxn],nf[maxn];
    int qpower(int a,int b){
    	int c=1;
    	while (b){
    		if (b&1) c=1LL*a*c%mo;
    		a=1LL*a*a%mo;
    		b>>=1;
    	}
    	return c;
    }
    int ni(int v){return qpower(v,mo-2);}
    int main(){
    	freopen(fin,"r",stdin);
    	freopen(fout,"w",stdout);
    	scanf("%d",&n);
    	fact[0]=1;
    	fo(i,1,maxn-1) fact[i]=1LL*fact[i-1]*i%mo;
    	fo(i,0,maxn-1) nf[i]=ni(fact[i]);
    	fo(i,1,n) scanf("%d",&a[i]);
    	memset(f,0,sizeof f);
    	f[1][0][0]=1;
    	fo(i,1,n)
    		fo(j,0,i-1)
    			fo(k,0,n){
    				f[i+1][j][k]=(f[i][j][k]+f[i+1][j][k])%mo;
    				fo(l,0,a[i]-1){
    					if (k+l>n) break;
    					f[i+1][j+1][k+l]=(f[i+1][j+1][k+l]+1LL*f[i][j][k]*nf[l])%mo;
    				}
    			}
    	printf("%d ",n);
    	fo(i,2,n) cout<<1LL*f[n+1][i][i-2]*fact[i-2]%mo<<" ";
    	return 0;
    }
    
  • 相关阅读:
    艰苦创业,无怨无悔,他靠养蜂开拓创业路!
    农民工如何拥有500多家加盟连锁店,看他是怎样做到的?
    从小面馆到餐饮王国,他的成功靠的是什么?
    夫妻合体创业,两月收入15万,他们是怎样做到的?
    农民王永宝,打造了一片乡村旅游乐土
    10年时间,从摆地摊到开连锁店,他们夫妻二人如何度过?
    F5 服务说明
    python 获取pool 成员状态
    CloudCC CRM探讨:精细流程管理与员工悟性培养
    CloudCC CRM探讨:精细流程管理与员工悟性培养
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6725236.html
Copyright © 2011-2022 走看看