zoukankan      html  css  js  c++  java
  • LOJ2526「HAOI2018」苹果树

    Description

    题目链接

    感觉题意已经挺清晰了,直接戳链接就行了

    Solution

    先考虑了在一个 (n) 个节点的树下接新的节点的计算方法,但发现如果一个树的形态不定那么路径长没法计算,所有接法的路径长之和也不具备什么规律

    考虑到一棵树上有两种特殊的点:叶子和根,既然接新节点行不通,那么我们考虑从根把一棵大的二叉树拆成两个小二叉树(可以为空)

    尝试了 (3) 种状态设计方法,最后还是选择了了一种最容易想到的设法,即设 (dp_i) 表示 (i) 个点所有可能的方案路径长的总和

    但是发现这样的设法有一点点麻烦的地方,就是还要考虑被分开的两个二叉树之间产生的贡献,即我们还要计算一个 (f_{i,j}) 数组,表示一个大小为 (i) 的二叉树在大小为 (j) 的二叉树中产生的贡献,在累加的时候还要多加上 (f_{i,j}(j-i-1)!+f_{j-i-1,j}i!)

    既然这样,我们不如让 (dp_i​) 表示 (i​) 个节点的子二叉树对全局((n​) 个节点的二叉树)产生的贡献,那么可以得到

    [dp_i=sumlimits_{j=0}^{i-1}dbinom{i-1}{j}(dp_j(i-j-1)!+dp_{i-j-1}j!+(i-j-1)!j!(j imes (n-j)+(i-j-1) imes (n-i+j+1))) ]

    怎么理解这个方程?

    首先,对于一个确定的二叉树,在生成完根节点后我们可以轮流生成它两个子树内的节点,那么一共会有 (inom{i-1}{j}) 种方案(因为两个子二叉树内的生成顺序已经固定)

    其次,我们知道 (dp_i) 表示的是所有 (i) 个节点的二叉树路径长的总和,若用 (c_j) 表示第 (j)(i) 个点的二叉树的路径总长,那么 (dp_i) 就相当于是加法原理得到的结果,即

    [dp_i=sumlimits_{j=1}^{i!}c_j ]

    其中 (i!)(i) 个节点一共可以生成 (i!) 种二叉树(形态可能有重复)

    现在假设我们已经以某种顺序生成了一棵左子树内有 (j) 个点,右子树内有 (i-j-1) 个点的二叉树

    那么它们各自内部对全局产生的贡献就是 (dp_j(i-j-1)!+dp_{i-j-1}j!) ,乘上的阶乘意为在一棵子树确定下来的情况下,另一棵子树会有阶乘种子树与之匹配

    现在我们还差根节点连向两棵子树的那两条边没有考虑,容易发现它们对全局的贡献就是 (j imes (n-j)+(i-j-1) imes (n-i+j+1)) ,还得乘上 ((i-j-1)!j!),因为两棵子树共有这么多种匹配方案,在每种匹配方案中我们都得加上这个贡献

    然后这题就做完了,复杂度 (O(n^2))

    其实这道题还是比较抽象的,需要好好想一想

    收获大概就是

    • 对于按照某种规则生成出来的所有的树(也可能是图之类的),要计算它们的一些值,这种题可以考虑在原来的树上加点,或是把原来的树从树根处拆成两个更小的树(感觉后者可能更适用些)
    • 如果某个元素对局部产生的贡献不好算(或者算起来比较麻烦),而最终要求的总体的贡献是要局部的贡献推过来的,那么可以直接考虑这个元素对全局产生的贡献
    • 一些比较抽象的题在想的时候一定要理清思路

    代码如下:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=2e3+10;
    int n,mod,c[N][N],fac[N],dp[N];
    inline int MOD(int x){x-=x>=mod? mod:0;return x;}
    inline void Add(int &x,int y){x+=y;x-=x>=mod? mod:0;}
    inline int Minus(int x){x+=x<0? mod:0;return x;}
    int main(){
    	scanf("%d%d",&n,&mod);
    	for(register int i=0;i<=n;i++){
    		c[i][0]=1;
    		for(register int j=1;j<=i;j++)
    			c[i][j]=MOD(c[i-1][j]+c[i-1][j-1]);
    	}
    	fac[0]=1;for(register int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
    	for(register int i=1;i<=n;i++)
    		for(register int j=0;j<i;j++)
    			Add(dp[i],1ll*c[i-1][j]*MOD(MOD(1ll*dp[j]*fac[i-j-1]%mod+1ll*dp[i-j-1]*fac[j]%mod)+1ll*fac[j]*fac[i-j-1]%mod*MOD(1ll*j*(n-j)%mod+1ll*(i-j-1)*(n-i+j+1)%mod)%mod)%mod);
    	printf("%d
    ",dp[n]);
    	return 0;
    }
    
  • 相关阅读:
    javascript 中检测数据类型的方法
    javascript 中的类数组和数组
    html5 构造网页的新方式
    关于 jQuery 中的 $.data() 方法和 jQuery 对象上的data 方法
    基于北洋PT站表结构分析以及ORM重写
    面试题准备
    sqlalchemy 外键
    sqlalchemy
    ansible roles
    ansible
  • 原文地址:https://www.cnblogs.com/ForwardFuture/p/11548763.html
Copyright © 2011-2022 走看看