bzoj 3244: Noi2013树的计数

题目大意就是给出dfs序和bfs序,求树的期望高度


首先感谢Mektpoy的blog:树的计数——Mektpoy


Mektpoy的做法是O(nlogn)的,已经讲得比较清楚了,但还是看了好久才想清楚,所以多说两句。


1.当pos[B] = pos[A] + 1时,若B是A的儿子,因为bfs序相邻,那么B必须是A的下一层的第一个儿子,所以在这层之前不可能出现过bfs大于B的,即在此之前的dfs序中不能出现比B大的数,还是一个RMQ问题。


2.因为懒得写线段树了,就用st表来求RMQ。Mektpoy说的:"对于dfs序在B后面的点 对于任意一个这些点中bfs序>B的点 他前面(也就是B和这个点之间)若有一个 < A的点也不可以",只需求出最后一个比B大的数就能满足任意了,这里利用st表来递归O(logN)的求出最后一个比B大的数,再求之间最小的数,与A比较即可。


好像还有O(N)的算法,应该是利用更巧妙的性质,懒得看了(好渣)


/**************************************************************
    Problem: 3244
    User: cabinfever
    Language: C++
    Result: Accepted
    Time:1016 ms
    Memory:35676 kb
****************************************************************/
 
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
 
using namespace std;
 
int dfs[200010];
int bfs[200010];
int haha[200010];
int pos[200010];
int n;
int st_max[200010][20];
int st_min[200010][20];
 
double ans = 0;
 
int find_b(int i,int j,int x)
{
    if(j == i)
        return j;
    int k = log2(j - i);
    if(st_max[j + 1 - (1 << k)][k] > x)
        return find_b(j+1-(1<<k),j,x);
    if(st_max[i][k] > x)
        return find_b(i,i+(1<<k)-1,x);
    return i;
}
 
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
        scanf("%d",&dfs[i]);
    for(int i = 1; i <= n; i++)
        scanf("%d",&bfs[i]);
    for(int i = 1; i <= n; i++)
    {
        haha[bfs[i]] = i;
        bfs[i] = i;
    }
    for(int i = 1; i <= n; i++)
    {
        dfs[i] = haha[dfs[i]];
        pos[dfs[i]] = i;
    }
    ans = 2;
    for(int i = 1; i <= n; i++)
    {
        st_min[i][0] = dfs[i];
        st_max[i][0] = dfs[i];
    }
    int k = log2(n) + 1;
    for(int j = 1; j < k; j++)
        for(int i = 1; i <= n; i++)
            if(i + (1 << j) <= n + 1)
            {
                st_min[i][j] = min(st_min[i][j-1],st_min[i + (1 << (j-1))][j-1]);
                st_max[i][j] = max(st_max[i][j-1],st_max[i + (1 << (j-1))][j-1]);
            }
    for(int i = 2; i < n; i++)
    {
        if(pos[i] > pos[i+1])
            ans++;
        else if(pos[i+1] > pos[i]+1)
            ans += 0;
        else
        {
            int j = find_b(pos[i+1],n,i+1);
            int k = log2(j - pos[i+1] + 1);
            int k2 = log2(pos[i]-1);
            int max2 = max(st_max[1][k2],st_max[pos[i] + 1 - (1 << k2)][k2]);
            if(min(st_min[pos[i+1]][k],st_min[j-(1<<k)+1][k]) < i || max2 > i + 1)
                ans += 0;
            else
                ans += 0.5;
        }
    }
    printf("%.3lf\n",(double)ans-0.001);
    printf("%.3lf\n",(double)ans);
    printf("%.3lf\n",(double)ans+0.001);    
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值