【NOIP2016提高A组模拟9.3】被粉碎的线段树

题目

这里写图片描述
Input

第一行包括两个正整数,N ,M ,分别表示线段树的宽以及询问次数。
以下N-1 行以先序遍历(dfs深搜顺序)描述一个小R线段树,每行一个正整数表示当前非叶子节点的 mid,保证每个节点L<=mid<=r 。
(因为叶子节点不需要mid ,所以在读入时走到叶子节点时回溯即可,所以共N-1 个mid ,而且保证1~N-1 各出现一次)
而后M 行每行包括两个正整数,L,r(1<=L<=r<=N) 描述一个要求区间定位的区间。

Output

M行每行包括一个正整数,表示给出的询问区间在给定的线段树上的区间定位个数。

Sample Input

7 3
4
3
1
2
5
6
3 6
2 7
1 6
Sample Output

4
3
3
【样例解释】
这里写图片描述
Data Constraint
这里写图片描述

比赛时の想法

比赛的时候思考了一会儿,然后想到了贪心,每一次左端点都尽可能往右跳,那么答案肯定是最优的,但是只有75分,并没有想到可以用倍增优化(其实想到了也打不出来)

正解

第一种是上述的贪心+倍增优化(好难打啊打不出来)
第二种我们需要先发现一个性质:
区间定位个数(答案) = 2 * 区间长度 - 完全被该区间包含的节点个数。
如果查询的区间为[l,r],那我们其实是查询所有完全在区间线段
那么就直接用二维偏序计数的思想解决问题(第一位排序,第二位用个线段树什么的)

贴代码

var
    tree:array[0..300005]of longint;
    i,j,k,l,n,m,tot,total,mid:longint;
    a:array[0..200005,1..2]of longint;
    b:array[0..200005,1..3]of longint;
    ans:array[0..100005]of longint;
procedure maketree(l,r:longint);
var
    mid:longint;
begin
    if l<>r then
    begin
        readln(mid);
        inc(k);
        a[k,1]:=l;
        a[k,2]:=mid;
        maketree(l,mid);
        inc(k);
        a[k,1]:=mid+1;
        a[k,2]:=r;
        maketree(mid+1,r);
    end;
end;
procedure qsort(l,r:longint);
var
    i,j,mid:longint;
begin
    i:=l;
    j:=r;
    mid:=b[(i+j) div 2,1];
    repeat
        while b[i,1]>mid do inc(i);
        while b[j,1]<mid do dec(j);
        if i<=j then
        begin
            b[0]:=b[i];
            b[i]:=b[j];
            b[j]:=b[0];
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then qsort(i,r);
    if l<j then qsort(l,j);
end;
procedure change(x,l,r,y:longint);
var
    mid:longint;
begin
    if (l=r) and (l=y)  then
    tree[x]:=tree[x]+1
    else
    begin
        mid:=(l+r) div 2;
        tree[x]:=tree[x]+1;
        if y<=mid then change(x*2,l,mid,y) else change(x*2+1,mid+1,r,y);
    end;
end;
procedure find(v,l,r,x,y:longint);
var
    mid:longint;
begin
    if x>y then exit;
    if (l=x) and (r=y) then
    begin
        tot:=tot-tree[v]
    end else
    begin
        mid:=(l+r) div 2;
        if y<=mid then find(v*2,l,mid,x,y) else
        if x>mid then find(v*2+1,mid+1,r,x,y) else
        begin
            find(v*2,l,mid,x,mid);
            find(v*2+1,mid+1,r,mid+1,y);
        end;
    end;
end;
begin
   // assign(input,'t3.in'); reset(input);
    readln(n,m);
    k:=1;
    a[1,1]:=1;
    a[1,2]:=n;
    maketree(1,n);
    for i:=1 to m do readln(b[i,1],b[i,2]);
    for i:=1 to m do b[i,3]:=i;
    qsort(1,m);
    k:=2*n-1;
    for i:=1 to m do
    begin
        for j:=k downto 1 do
        if a[j,1]>=b[i,1] then
        begin
            change(1,1,n,a[j,2]);
            inc(total);
            if j=1 then k:=-1;
        end else break;
        if k=-1 then
        begin
            k:=0;
            j:=0;
        end else k:=j;
        tot:=total;
        find(1,1,n,b[i,2]+1,n);
        ans[b[i,3]]:=2*(b[i,2]-b[i,1]+1)-tot;
    end;
    for i:=1 to m do writeln(ans[i]);
   // close(input);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值