zoukankan      html  css  js  c++  java
  • 洛谷 U140603 树的平均

    洛谷 U140603 树的平均

    洛谷传送门

    题目背景

    SeawaySeawa**y太菜了。当大佬们都在爆切插头DP,计算几何,反演,Matrix-Tree的时候,SeawaySeawa**y连最基本的搜索都打不对......

    菜归菜,SeawaySeawa**y还是有一些基本常识的:他知道,对于一棵有根树,可以进行两种不同的遍历方式:DFS和BFS。这两种遍历方式在遍历过程中会分别生成两种序列:DFS序和BFS序。SeawaySeawa**y惊奇地发现:对于两棵不同的树,它们的DFS序有可能相同,它们的BFS序同样有可能相同。SeawaySeawa**y大呼绝妙,并觉得这里大有文章可做。

    题目描述

    现在,SeawaySeawa**y给出一个DFS序和一个BFS序。他想知道,在符合这两个限制条件的所有有根树中,树的高度的平均值。即:假如有NN棵不同的树具有这组DFSDFS序和BFSBFS序,且它们的高度分别为:h_1,h_2,cdots,h_nh1,h2,⋯,h**n,那么请你求出下式的值:

    frac{sum_{i=1}^{i=N}h_i}{N}Ni=1i=Nhi

    这里的树的高度是指:树的最深深度。

    输入格式

    从文件average.inaverage.i**n中读入数据。

    第一行一个整数NN,表示树的节点个数。

    第二行有一个1-N1−N的排列,描述树的DFS序。

    第三行有一个1-N1−N的排列,描述树的BFS序。

    输出格式

    输出到文件average.outaverage.out中。

    一行输出一个实数,保留三位小数。表示答案。你能够获得本测试点的分数,当且仅当你的答案与标准输出的差的绝对值小于等于0.0010.001。


    题解:

    一开始看到这道题还是没什么思路的。因为感觉没什么办法来通过序列来还原树。反正就是不知道咋暴力。

    既然不知道暴力,就去想正解吧。没办法了。

    发现,虽然序列还原树没什么思路,但是可以显然地发现一个性质:BFS序分几层,树的高度就是多少。也就是说在BFS序上枚举断点即可。

    兴冲冲地去做,发现还不是那么的简单,因为情况大抵有三种:必须分,必须部分,可分可不分。

    然后想想如何判断这三种情况。

    首先,对于bfs序连续的两个点,不是在同一层,就是在下一层。那么这个条件可以用DFS序约束,如果BFS序连续,但是较大BFS序的DFS序反而较小,那么就一定被挪到下一层去了,这个断点位置的贡献为1.反之,贡献不一定为0,也有可能是0.5,所以确定不了。

    想到这里兴高采烈了好半天,但是其实还远远未结束。

    因为只考虑了DFS对BFS序的约束,BFS序必然也会对DFS序有约束。

    那么,对于DFS序连续的两个点,有可能是父子关系,也有可能都是叶子,是兄弟关系。甚至有可能,是其一个点某一个祖先的儿子。

    如果是兄弟关系或者父子关系,那么其BFS序也应该是连续的。也就是它们的深度差不能大于1。它们的BFS序之间最多分1层。这样的话,可以用一个差分来维护区间的这个信息。

    差分维护的是分多少层。

    但是如果是祖先关系,那么其BFS序就会出现逆序的情况。也就是较大DFS序的BFS序反而较小。

    代码:

    #include<cstdio>
    using namespace std;
    const int maxn=3e5+5;
    int n;
    int bfn[maxn],dfn[maxn],bp[maxn],dp[maxn],s[maxn];
    double ans;
    int main()
    {
    	freopen("average.in","r",stdin);
    	freopen("average.out","w",stdout);
    	scanf("%d",&n);
    	ans=2; 
        ++s[1];
        --s[2];
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&dfn[i]);
    		dp[dfn[i]]=i;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&bfn[i]);
    		bp[bfn[i]]=i;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		dfn[i]=bp[dfn[i]];
    		bfn[i]=dp[bfn[i]];
    	}
    	for(int i=1;i<n;++i) 
          	if(bfn[i]>bfn[i+1]) 
            	++s[i],--s[i+1],++ans;
        for(int i=1;i<n;++i) 
          	if(dfn[i]+1<dfn[i+1]) 
            	++s[dfn[i]],--s[dfn[i+1]];
    	int w=0;
        for(int i=1;i<n;++i) 
          	w+=s[i],ans+=w?0:0.5;
    	printf("%.3lf
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    虚方法表与动态分派机制
    方法重载与invokevirtual字节码指令的关系
    栈桢与操作数栈以及符号引用与直接引用的转换
    通过字节码分析this关键字以及异常表的作用
    JVM synchronized关键字所生成的字节码
    window Chrome 下允许跨域访问服务端接口设置
    JVM Java字节码方法表与属性
    JVM 字节码的结构
    Jar hell问题以及解放方法
    JVM 线程上下文类加载器
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13994366.html
Copyright © 2011-2022 走看看