Gym 102091K The Stream of Corning 2 (线段树+离线)

<题目链接>

题目大意:

进行两种操作:1.给定一个数的出现时间、价值、消失时间;

       2.进行一次询问,问你当前时间,第K大的数的价值。

解题分析:

采用离线集中处理,将每个数的出现时间和它的消失时间分组存下,并且存下所有的询问。然后就是依次进行所有询问,将这些询问时间点之前的点插入,将已经消失的点删除。因为算了一下复杂度感觉能过,所以就没有用离散化,线段树的代表区间大小就是1~1e6,线段树的节点维护的信息就是这个节点所对应区间的有效节点个数。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+5 , MAX = 1e6+5;

#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
struct Node{
    int time,val;
    Node (int _time=0,int _val=0):time(_time),val(_val){}
    bool operator < (const Node & tmp) const { return time<tmp.time; }
}node1[N],node2[N],d[N];

int tr[MAX<<2];

template<typename T>
inline void read(T&x){
    x=0;int f=1;char ch=getchar();
    while(ch<'0' ||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); }
    while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
    x*=f;
}

inline void Pushup(int rt){ tr[rt]=tr[rt<<1]+tr[rt<<1|1]; }
void update(int rt,int l,int r,int loc,int val){
    if(l==r){ tr[rt]+=val;return; }
    int mid=l+r>>1;
    if(loc<=mid)update(lson,loc,val);
    if(loc>mid)update(rson,loc,val);
    Pushup(rt);
}
int query(int rt,int l,int r,int k){
    if(l==r)return l;
    int mid=l+r>>1;
    if(tr[rt<<1]>=k)return query(lson,k);
    else if(tr[rt<<1]<k)return query(rson,k-tr[rt<<1]);    //左子树不够k个,则第k个一定在右子树
}
int main(){
    int T,ncase=0;scanf("%d",&T);
    while(T--){
        printf("Case %d:
",++ncase);
        int q;scanf("%d",&q);
        int cnt1=0,cnt2=0,cnt3=0;
        while(q--){
            int op;read(op);
            if(op==1){
                int s,e,w;read(s);read(w);read(e);
                cnt1++,cnt2++;
                node1[cnt1]=Node(s,w);     //node1记录开始时间
                node2[cnt2]=Node(e,w);     //node2记录结束时间
            }else {
                int now,k;read(now),read(k);
                cnt3++;
                d[cnt3].time=now,d[cnt3].val=k;     //记录下询问数组
            }
        }
        sort(node1+1,node1+1+cnt1);sort(node2+1,node2+1+cnt2); sort(d+1,d+1+cnt3);   
        int p1=1,p2=1;    //遍历‘建立’和‘删除’这两个数组的指针
        memset(tr,0,sizeof(tr));     //建树
        for(int i=1;i<=cnt3;i++){
            int now=d[i].time,k=d[i].val;
            while(p1<=cnt1 && node1[p1].time<=now){   //将当前时间前存活的的点插入线段树
                update(1,1,MAX,node1[p1].val,1);
                p1++;
            }
            while(p2<=cnt2 && node2[p2].time<now){    //将当前时间前死亡的点删除线段树
                update(1,1,MAX,node2[p2].val,-1);
                p2++;
            }
            if(tr[1]<k){ puts("-1");continue; }    //如果不足k个存活,直接输出-1
            printf("%d
",query(1,1,MAX,k));
        }
    }
}
原文地址:https://www.cnblogs.com/00isok/p/10514081.html