牛客网暑期ACM多校训练营(第六场) I题:Team Rocket (线段树)

 

题目大意:

一维坐标系上有n条线段,每条线段都有 l r 。每次破坏一个点x 所有包含这个点x 的线段都会被破坏。现在要求你针对每次询问输出每次有多少条线段被破坏,被破坏过的线段不能被破坏(强制在线)。

m次询问完后统一输出每条线段分别是在第多少次操作后被破坏的,没有被破坏的输出0。

 

解题思路:

比赛的时候想到了 一种思路,就是按照每条线段的左端点排序,然后针对右端点建立主席树,每次查询有多少右端点大于等于x值,但是当时发现主席树的修改很麻烦,然后没有写出来。

结果赛后发现是真的智障了,干嘛要建主席数,问了我校主席cyh,他也是先按照左端点排序,然后直接针对右端点建立线段树即可。因为我们可以二分找到我们每次需要查询的 l r,在 l r 这个区间内,直接查询有多少val大于x 并且把val更新为-INF即可,每个结点保存右端点最大值。

 

Ac代码:

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
#define rank ra   
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int INF=1e9+7;
const int mod=998244353;
int n,m,r,num,rank[maxn]; //rank表示i条线段什么时候被破坏
ll res;
vector<int> v;
struct node //线段
{
    int l,r,idx;
    bool operator<(const node &p) const
    {
        if(l==p.l) return r<p.r;
        return l<p.l;
    }
}a[maxn];
struct Node //线段树结点
{
    int l,r,mid,id; //以及最大值对应的线段
    int val;    //最大值
}t[maxn<<2];
void pushup(int rt) //维护最大值 以及 线段编号
{
    t[rt].val=max(t[lson].val,t[rson].val);
    if(t[lson].val>=t[rson].val) t[rt].id=t[lson].id;
    else t[rt].id=t[rson].id;
}
void build(int l,int r,int rt)
{
    int mid=(l+r)>>1;
    t[rt].l=l,t[rt].r=r,t[rt].mid=mid;
    if(l==r)
    {
        t[rt].val=a[l].r;
        t[rt].id=a[l].idx;
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    pushup(rt);
}
void update(int l,int r,int x,int ti,int rt)    //查询以及更新
{
    if(l>r) return ;
    if(t[rt].val<x) return ;    //小于 x return
    if(t[rt].l==t[rt].r)
    {
        num++;
        t[rt].val=-INF;
        rank[t[rt].id]=ti;
        if(res==0) res=1;
        res=res*t[rt].id%mod;   //维护res
        return ;
    }
    if(l<=t[rt].mid) update(l,r,x,ti,lson);
    if(r>t[rt].mid) update(l,r,x,ti,rson);
    pushup(rt);
}
int main()
{
    int QAQ,kase=0;
    scanf("%d",&QAQ);
    while(QAQ--)
    {
        v.clear();
        memset(rank,0,sizeof rank);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&a[i].l,&a[i].r),a[i].idx=i,v.push_back(a[i].l);
        sort(a+1,a+1+n),sort(v.begin(),v.end());    //对线段排序 
        build(1,n,1);
        int ti=1; num=0; res=0;
        printf("Case #%d:\n",++kase);
        while(m--)
        {
            int x; scanf("%d",&x);
            x=x^res;
            //printf("--%d %lld\n",x,res);
            auto it=upper_bound(v.begin(),v.end(),x);   //找出应该查询的区间 r 
            if(it==v.end()) r=n;
            else if(it==v.begin()) r=0;
            else r=it-v.begin();
            num=0; res=0;
            update(1,r,x,ti++,1);
            printf("%d\n",num);
        }
        for(int i=1;i<=n;i++)
            printf("%d%c",rank[i]," \n"[i==n]);
    }
    //system("pause");
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值