zoukankan      html  css  js  c++  java
  • 【JZOJ4747】【NOIP2016提高A组模拟9.3】被粉碎的线段树

    题目描述

    题目描述

    输入

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

    输出

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

    样例输入

    7 3
    4
    3
    1
    2
    5
    6
    3 6
    2 7
    1 6

    样例输出

    4
    3
    3

    数据范围

    数据范围

    样例解释

    样例解释
    给定线段树样子如上图。
    区间[3,6] 定位出的区间是[3,3][4,4][5,5][6,6] ,共四个。
    区间[2,7] 定位出的区间是[2,3],[4,4],[5,7] ,共三个。
    区间[1,6] 定位出的区间是[1,4],[5,5],[6,6] ,共两个。

    解法

    性质:[l,r]的答案=(r-l+1)*2-k (k为[l,r]包含的线段数)
    证明:对于线段树里面的线段[l1,r1],这个线段所包含的线段共(r1-l1+1)*2-1。
    [l,r]的答案=[l,r]中包含的极大线段,其中每个极大线段都会贡献(r1-l1+1)*2-1个线段被区间包含。
    假设[l,r]中有n条极大线段,那么[l,r]所包含的线段就有(r-l+1)*2-n条。
    所以n=(r-l+1)*2-[l,r]包含的线段数。
    实现:排序后使用树状数组统计区间包含的线段数。

    代码

    #include<iostream>
    #include<stdio.h>
    #include<math.h>
    #include<string.h>
    #include<algorithm>
    #define ll long long
    #define ln(x,y) int(log(x)/log(y))
    #define sqr(x) ((x)*(x))
    using namespace std;
    const char* fin="aP3.in";
    const char* fout="aP3y.out";
    const int inf=0x7fffffff;
    const int maxn=100007,maxm=maxn*2;
    int n,m,i,j,k,l,r,tot,mid,deep;
    struct qj{
        int l,r,id;
    }a[maxm],stack[maxm],b[maxm];
    int cd[maxm],ha[maxm];
    int c[maxn],ans[maxm];
    void add(int a1,int b,int c){
        if (c==1) {
            a[++tot].l=a1;
            a[tot].r=b;
        }
        if (a1==b) return ;
        stack[++deep].l=a1;
        stack[deep].r=b;
        cd[deep]=c;
    }
    bool cmp(qj a,qj b){
        return a.l<b.l;
    } 
    void change(int v,int v1){
        for (;v<=n;v+=v&(-v)) c[v]+=v1;
    }
    int getsum(int v){
        int k=0;
        for (;v;v-=v&(-v)) k+=c[v];
        return k;
    }
    int main(){
        scanf("%d%d",&n,&m);
        add(1,n,1);
        while (deep){
            if (cd[deep]==1){
                scanf("%d",&ha[deep]);
                deep--;
                add(stack[deep+1].l,stack[deep+1].r,0);
                add(stack[deep].l,ha[deep],1);
            }else{
                deep--;
                add(ha[deep+1]+1,stack[deep+1].r,1);
            }
        }
        sort(a+1,a+tot+1,cmp);
        for (i=1;i<=m;i++){
            scanf("%d%d",&b[i].l,&b[i].r);
            b[i].id=i;
        }
        sort(b+1,b+m+1,cmp);
        j=tot;
        k=m;
        for (i=n;i>=1;i--){
            while (j && i==a[j].l) change(a[j--].r,1);
            while (k && i==b[k].l) {
                ans[b[k].id]=2*(b[k].r-b[k].l+1)-getsum(b[k].r);
                k--;
            }
        }
        for (i=1;i<=m;i++) printf("%d
    ",ans[i]);
        return 0;
    }

    启发

    计算答案时:
    1.答案由什么构成。
    2.本题中什么东西易求。
    3.把1中的东西转化为2中的求得答案。
    例如本题:
    1.显然[l,r]的答案即为包含的极大线段数。
    2.[l,r]的所包含的线段数容易求得。
    3.极大线段数=(r-l+1)*2-[l,r]的所包含的线段数。
    完成转化,本题解决。

  • 相关阅读:
    [To be translated] Nova:libvirt image 的生命周期
    Neutron 理解(5):Neutron 是如何向 Nova 虚机分配固定IP地址的 (How Neutron Allocates Fixed IPs to Nova Instance)
    Cinder 调试
    SSH 无密码访问其它机器 和 Windows 上 putty 无密码访问 Linux 机器
    Hadoop 数据库
    Hadoop 分布式文件系统
    C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别
    Google Scholar 论文参考文献的自动生成
    Linux shell ${}简单用法
    C/C++ 获取目录下的文件列表信息
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6714906.html
Copyright © 2011-2022 走看看