zoukankan      html  css  js  c++  java
  • [COCI2017.1]Deda —— 解锁线段树的新玩法

    众所周知,能用线段树做的题一定可以暴力
    但考场上也只能想到暴力了,毕竟还是对线段树不熟练。

    deda

    描述
    有一辆车上有n个小孩,年龄为1~n,然后q个询问,M X A代表在第X站时年龄为A的小孩会下车,D Y B代表询问年龄大于等于B且在第Y站(包含第Y站)以前下车的年龄最小的小孩,如果不存在,则输出-1。
    .
    输入
    输入的第一行包含正整数N和Q,孩子的数量和询问的数量。
    接下来Q行,每行3个值,第一个值为M或者D,询问有两种分别用M和D表示,M表示下车操作,D表示询问
    如果是M X A,那么三个值的含义是:M X A代表在第X站时年龄为A的小孩会下车
    如果是D Y B,代表询问年龄大于等于B且在第Y站(包含第Y站)以前下车的年龄最小的小孩,如果不存在,则输出-1。
    所有询问都对应着不同的孩子,输入中至少有一行是询问的问题。
    .
    输出
    对于每个询问的问题,输出答案。如果没有答案,输出-1。
    .
    分数分布
    对于100%的数据:2≤N,Q≤200000,1≤X,Y≤1,000,000,000,1≤A,B≤N。
    .
    样例输入1
    3 4
    M 10 3
    M 5 1
    D 20 2
    D 5 1
    .
    样例输出1
    3
    1
    .
    样例输入2
    10 10
    M 20 10
    D 1 9
    M 2 3
    D 17 10
    M 20 2
    D 8 2
    M 40 1
    D 25 2
    M 33 9
    D 37 9
    样例输出2
    -1
    -1
    3
    2
    9

    先来说说我的暴力做法吧(虽然这个做法只能40分,而且写出来有点蠢)

    这道题我的第一反应是离线做,将每个询问的坐标从小到大排序依次处理,这样就可以保证"(X<Y)"这个条件了。

    然而正解却直接用的是线段树。。。

    根据数据范围,线段树表示的区间不能是坐标(因为1≤X,Y≤1,000,000,000)。所以我们可以用L-R来表示年龄为L岁到R岁的小孩中,他们下车的位置,坐标最小的那一个。

    为什么是坐标最小的那一个呢,因为在询问中,

    D Y B代表询问年龄大于等于B且在第Y站(包含第Y站)以前下车的年龄最小的小孩

    既然要在第(Y)站之前,那么存进最小的位置肯定不会错。在查询时,我们只需要判断在当前区间(L-R)中,它的最小位置X是否小于(Y),如果是小于,我们继续,如果是大于,则return掉。

    详情请见代码(线段树这种东西,不看代码是懂不了的)

    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define N 200010
    
    int n,m,minv[N*10],x,age,ql,qr,ans=N+10;
    char a;
    
    void Insert(int i,int L,int R) { //插入操作
        if(L==R) {minv[i]=x; return ; }
        int mid=(L+R)/2;
        if(age<=mid) Insert(i*2,L,mid);
        else Insert(i*2+1,mid+1,R);
        minv[i]=min(minv[i*2],minv[i*2+1]);
    }
    
    int Find(int i,int L,int R) { 
        if(minv[i]>x) return N+10;
        if(L==R) return L; //L-R表示的年龄,所以要返回这个答案
        int mid=(L+R)/2;
        if(minv[i*2] <= x ) return Find(i*2,L,mid);
        else return Find(i*2+1,mid+1,R);
    }
    
    void Query(int i,int L,int R) {
        if(ql<=L && qr>=R) {ans=Find(i,L,R); return ;} //找到符合条件的区间
        int mid=(L+R)/2;
        if(mid>=ql) {Query(i*2,L,mid); if(ans!=N+10) return ;}//如果找到了答案,那么
        if(qr>mid) Query(i*2+1,mid+1,R);				//(mid+1)-R的区间里的答案只会更大
    }																		//所以return 掉
    
    int main() {
        memset(minv,0x3f,sizeof(minv));
        cin>>n>>m;qr=n;
        for(int i=1;i<=m;i++) {
            cin>>a>>x>>age;
            if(a=='M') Insert(1,1,n);
            else { ql=age; Query(1,1,n); if(ans==N+10) cout<<"-1"<<endl; else cout<<ans<<endl; }
            ans=N+10;
        }
    }
    

    这道题的难点我认为是将l-r区间表示为年龄l-r的小朋友。

    因为在平常线段树中,我们是在节点中存的答案,而这道题的做法却不一样。所以线段树是一个十分灵活的数据结构。

  • 相关阅读:
    263 相对布局之3— 相对布局的综合案例
    262 相对布局之2— 相对布局的属性设置
    leetcode-----110. 平衡二叉树
    leetcode-----109. 有序链表转换二叉搜索树
    leetcode-----108. 将有序数组转换为二叉搜索树
    leetcode-----107. 二叉树的层次遍历 II
    leetcode-----106. 从中序与后序遍历序列构造二叉树
    leetcode-----105. 从前序与中序遍历序列构造二叉树
    leetcode-----104. 二叉树的最大深度
    leetcode-----103. 二叉树的锯齿形层次遍历
  • 原文地址:https://www.cnblogs.com/MisakaMKT/p/11252265.html
Copyright © 2011-2022 走看看