这题大家为什么都写O(NlogN)的算法呢?……

让本蒟蒻来写一个O(N)的吧……

首先还是对BFS序和DFS序重编号,记标好的DFS序为d[1..n]。令pos[x]为x在d[]中出现的位置,即pos[d[i]]=i。

然后还是要用到一个BFS序的分段对应一棵树的结论……然后我们考察一个分段方式的合法性:首先结点1是唯一的根必须要单独一段;其次,BFS序中一层的结点出现的顺序和DFS序中的顺序一定是相同的,因此对于任何的一段[l, r],都有pos[l]<pos[l+1]<pos[l+2]<…<pos[r];最后我们还需要考虑结点深度的约束,对于DFS序中两个相邻结点,后一个结点的深度至多比前一个结点大1。综上所述,我们得到了以下三条约束:

(1)结点1单独分为一段;

(2)对于任何一段[l, r],pos[l]<pos[l+1]<pos[l+2]<…<pos[r];

(3)记depth[i]为结点i的深度,则depth[d[i+1]]<=depth[d[i]]+1。

我们尝试转化上面3条约束。建立数组s[1..n-1],s[i]=1当且仅当结点i和结点i+1分在不同的两段,否则s[i]=0。这样进行转化之后,以上三条约束变成了如下的形式:

(1)s[1]=1;

(2)若pos[i]>pos[i+1],则s[i]=1;

(3)若d[i]<d[i+1],则Σ{s[j] | j=d[i]..d[i+1]-1}<=1。

第一条转化是容易理解的。第三条转化也容易理解,因为s[d[i]]、s[d[i]+1]、…、s[d[i+1]-1]的和就等于结点d[i]和d[i+1]的高度差。当d[i]>d[i+1]时,由于d[i]的BFS序在d[i+1]后面,所以depth[d[i+1]]<=depth[d[i]]<depth[d[i]]+1。第二条的转化是把原先的描述反过来理解形成的,也就是说若pos[i]>pos[i+1],那么结点i一定不满足约束,所以必须分为两段。可以证明这样的转化与原来的约束是等效的。

显然,1+Σs[i]就是树高。

我们考虑在O(N)时间内计算出树高的期望。约束1和约束2把一些s[i]固定为1,它们对s[]造成的影响可以非常容易地O(N)处理出来。然后考虑第三类约束,首先我们处理所有Σ{s[j] | j=d[i]..d[i+1]-1}=1的第三类约束,这类约束固定了一个区间的值(某一个s[i]=1,其余s[i]均为0)。这些值可以通过前缀和的处理技巧O(1)地打好固定标记,所以处理这些约束也是O(N)的。

最后我们剩下一些Σ{s[j] | j=d[i]..d[i+1]-1}=0的约束。由于每一个s[j]都是0,这意味着对于所有的j=l..r-1,都有pos[j]<pos[j+1](否则处理第二类约束时会使得某个s[j]=1,从而不满足Σs[j]=0)。因此,我们有pos[l]<pos[l+1]<pos[l+2]<…<pos[r]。注意到这里l=d[i]而r=d[i+1],所以pos[l]=i而pos[r]=i+1。也就是说这些剩下的约束其实都是一些形如i<i+1的恒成立的不等式,直接无视掉就好了。这样就处理完了所有的约束,最后我们还剩下一些位置没有被固定,这些位置取0或1均可,对答案的贡献就是0.5。

于是我们就可以O(N)地解决这个问题。代码异常的简洁,线段树什么的根本不用写嘛……

 /**************************************************************
Problem: 3244
User: IcyF
Language: C++
Result: Accepted
Time:216 ms
Memory:6276 kb
****************************************************************/ #include <cstdlib>
#include <cstdio> using namespace std; #define rep(n) for (int i = 1; i <= n; ++i)
#define sum(l, r) (s[r] - s[l - 1]) const int MAXN = ;
int n, a[MAXN], b[MAXN], c[MAXN], pos[MAXN], x[MAXN], s[MAXN], fix[MAXN];
int main()
{
scanf("%d", &n);
rep(n) scanf("%d", a + i);
rep(n) scanf("%d", b + i);
rep(n) c[b[i]] = i;
rep(n) a[i] = c[a[i]];
rep(n) pos[a[i]] = i; x[] = , ++fix[], --fix[];
rep(n - )
if (pos[i] > pos[i + ])
x[i] = , ++fix[i], --fix[i + ];
rep(n - )
s[i] = s[i - ] + x[i];
rep(n - )
if (a[i] < a[i + ])
{
int t = sum(a[i], a[i + ] - );
if (t)
{
++fix[a[i]];
--fix[a[i + ]];
}
} double ans = ; int f = ;
rep(n - )
{
f += fix[i];
if (f)
ans += double(x[i]);
else
ans += 0.5;
}
printf("%.3lf\n", ans + 0.999);
printf("%.3lf\n", ans + 1.0);
printf("%.3lf\n", ans + 1.001);
return ;
}

[BZOJ3244][NOI2013]树的计数的更多相关文章

  1. BZOJ3244 NOI2013树的计数(概率期望)

    容易发现的一点是如果确定了每一层有哪些点,树的形态就确定了.问题变为划分bfs序. 考虑怎样划分是合法的.同一层的点在bfs序中出现顺序与dfs序中相同.对于dfs序中相邻两点依次设为x和y,y至多在 ...

  2. [bzoj3244][noi2013]树的计数 题解

    UPD: 那位神牛的题解更新了,在这里. ------------------------------------------------------------------------------- ...

  3. BZOJ3244 [Noi2013]树的计数 【数学期望 + 树遍历】

    题目链接 BZOJ3244 题解 不会做orz 我们要挖掘出\(bfs\)序和\(dfs\)序的性质 ①容易知道\(bfs\)序一定是一层一层的,如果我们能确定在\(bfs\)序中各层的断点,就能确定 ...

  4. [UOJ#122][NOI2013]树的计数

    [UOJ#122][NOI2013]树的计数 试题描述 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的 DFS 序以及 BFS 序.两棵不同的树的 DFS 序 ...

  5. 【BZOJ3244】【UOJ#122】【NOI2013]树的计数

    NOI都是酱的题怎么玩啊,哇.jpg 原题: 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的DFS序以及BFS序.两棵不同的树的DFS序有可能相同,并且它们的 ...

  6. BZOJ3244/UOJ122 [Noi2013]树的计数

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  7. [bzoj3244] [洛谷P1232] [Noi2013] 树的计数

    Description 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的DFS序以及BFS序.两棵不同的树的DFS序有可能相同,并且它们的BFS序也有可能相同, ...

  8. 3244: [Noi2013]树的计数 - BZOJ

    Description 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的DFS序以及BFS序.两棵不同的树的DFS序有可能相同,并且它们的BFS序也有可能相同, ...

  9. bzoj 3244: [Noi2013]树的计数

    Description 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的DFS序以及BFS序.两棵不同的树的DFS序有可能相同,并且它们的BFS序也有可能相同, ...

随机推荐

  1. mysql 复合查询语句

    INSERT INTO runwa(rshottime,rmoney,renamecount) VALUES (CURDATE(),(select SUM(MONEY) from income whe ...

  2. Redis在实际项目中的一应用场景

    1.在游戏的等级排名,可以将用户信息放入到redis的有序集合中,然后取得相应的排名,不用自己写代码去排序. 2.利用rediss的数据特性的自增,自减属性,可以将项目中的一些列入阅读数,点赞数放入到 ...

  3. 001-linux scp文件拷贝

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器 ...

  4. PLSQLDeveloper安装与配置

    1.前提:首先要有oracle数据库或者有oracle服务器,才可以实现使用PLSQL Developer 工具连接到oracle数据库进行开发 2.下载PLSQLDeveloper并解压 3.配置环 ...

  5. 新建虚拟机与本机ping不通(一招解决)

    初始新建虚拟机或者复制虚拟机后,发现虚拟机能ping通内外网,但是本机无法ping通虚拟机,xshell也无法连接虚拟机 这时候就很头疼了,因为要上传很多文件到虚拟机上面 解决办法: 1.关闭虚拟机后 ...

  6. PHP 基础篇 - PHP 的 BC MATH 系列数学函数

    一.常见问题 用 PHP 做计算时经常会遇到精度带来的问题,下面来看两个常见的例子: 1. 运算比较 下面表达式输出的结果不是相等: <?php echo 2.01 - 0.01 == 2 ? ...

  7. flask内置session原理

    内置session原理 请求到来 当请求进来之后,先执行Flask对象的 __call__ 方法 def wsgi_app(self, environ, start_response): # 获取请求 ...

  8. python全栈开发从入门到放弃之socket并发编程多线程

    一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 二 开启线程的两种方式 from threadi ...

  9. [笔记] 基于nvidia/cuda的深度学习基础镜像构建流程

    基于NVidia开源的nvidia/cuda image,构建适用于DeepLearning的基础image. 思路就是先把常用的东西都塞进去,再装某个框架就省事儿了. 为了体验重装系统的乐趣,所以采 ...

  10. PKU 3318 Matrix Multiplication(神奇的输入)

    #include<cstdio> using namespace std; ][]; ][],C[][]; int Read() { ; ; while((ch=getchar())==' ...