【Nowcoder】暑期多校day6 Team Rocket (线段树 细节)

题目大意

有 n 条线段,分别对 m 个点进行操作,每次操作时删除包括这个点的所有线段,每个线段至多被删除一次。问每个操作删除的线段数,以及每个线段被消灭的最早一次操作。


解题思路

考虑把每个区间当做平⾯上的⼀个点 (l, r),每次操作相当于是删去所有的满⾜ (l <= x <= r) 的点,相当于是把横坐标 <= x 的所有纵坐标 >= x 的点删去。

离散化横坐标后建线段树,每个节点⽤⼀个 vector 保存所有左端点在区间内,按右端点从⼩到⼤排序的点。如果⽤归并的⽅法初始化,时间复杂度和保存的线段总数均为 O(nlogn)。

每次询问时在线段树上⾛到 [1,x] 要问的节点,将右端点不⼩于 x 的线段都从该节点的 vector 中删去。同时删的时候查看该线段是否为第⼀次从线段树中删除,是则更新答案。

因为线段树中的每个节点最多被删除⼀次,最终的时间复杂度就是 O(nlogn)。

由于每次操作影响的是⼀个后缀,也可以⽤常数更⼩的树状数组实现,但初始化时注意不能多 log。


代码

因为写代码的时候不专心,这个代码被我调了一下午…

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

inline int read() {
    register int val=0, sign=1; char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && ch!='-'); ch=='-'?sign=-1:val=ch-'0';
    while(~(ch=getchar()) && (ch>='0' && ch<='9')) val=(val<<1)+(val<<3)+ch-'0';
    return val*sign;
}

const int maxn=int(2e5)+11, moder=998244353;
const int mul(const int &a,const int &b) {return 1ll*a*b%moder;}

struct seg {
    int x,y,id;
    seg() {}
    void input(int id_) {x=read(), y=read(), id=id_;}
    bool operator < (const seg &rhs) const {
        if(x==rhs.x) return y<rhs.y;
        else return x<rhs.x;
    }
}q[maxn];

int n,m;
int xp[maxn], tot=0;
int used[maxn];

#define ls ((k)<<1)
#define rs (ls|1)
#define mid ((l+r)>>1)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
typedef pair<int,int> pii;

vector<pii> v[maxn<<2];
int it=1, pt[maxn<<2];

bool cmp(const pii &a,const pii &b) {
    if(a.fi==b.fi) return a.se<b.se;
    else return a.fi<b.fi;
}

void build(int k=1,int l=1,int r=tot) {
    v[k].clear();
    if(l==r) {
        for(;it<=n && q[it].x==l;++it) 
            v[k].pb(mp(q[it].y,q[it].id));
        pt[k]=v[k].size()-1;
        return;
    }
    build(ls,l,mid), build(rs,mid+1,r);
    unsigned int tl=0, tr=0;
    while(tl<v[ls].size() && tr<v[rs].size())
        v[k].pb((v[ls][tl]<=v[rs][tr])?v[ls][tl++]:v[rs][tr++]);

    while(tl<v[ls].size()) v[k].pb(v[ls][tl++]);
    while(tr<v[rs].size()) v[k].pb(v[rs][tr++]);
    pt[k]=v[k].size()-1;

    return;
}

int L=1,R,VAL,IND;
int prod=1;

int query(int k=1,int l=1,int r=tot) {
    int res=0;
    if(L<=l && r<=R) {
        while(pt[k]>=0 && VAL<=v[k][pt[k]].fi) {
            if(!used[v[k][pt[k]].se]) {
                res++, used[v[k][pt[k]].se]=IND;
                prod=mul(prod,v[k][pt[k]].se);
            }
            pt[k]--;
        }
        return res;
    }
    if(L<=mid) res+=query(ls,l,mid);
    if(R> mid) res+=query(rs,mid+1,r);
    return res;
}

void work() {
    n=read(), m=read();
    register int i;
    for(i=1;i<=n;++i) {
        q[i].input(i);
        used[i]=0;
    }
    sort(q+1,q+1+n);

    for(i=1,tot=0;i<=n;++i) {
        xp[i]=q[i].x;
        if(i>1 && xp[i]==xp[i-1]) q[i].x=q[i-1].x;
        else q[i].x=++tot;
    }
    tot=unique(xp+1,xp+1+n)-(xp+1);
    it=1, build();

    int res=0, last=0;
    for(IND=prod=1;IND<=m;++IND,prod=1) {
        VAL=read()^last;
        R=upper_bound(xp+1,xp+1+tot,VAL)-xp-1;
        res=R?query():0;
        last=res?prod:0;
        printf("%d\n",res);
    }

    for(i=1;i<=n;++i)
        printf("%d%c",used[i]," \n"[i==n]);
    return;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input0.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif
//  std::ios::sync_with_stdio(false);
    int T=read();
    for(int i=1;i<=T;++i) {
        printf("Case #%d: \n",i);
        work();
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值