牛客网暑期ACM多校训练营(第六场)-- I-Team Rocket

题意:

有n条线段,每条线段都有l,r。每次破坏一个点x所有包含这个点x 的线段都会被破坏。现在要求你输出每次有多少条线段被破坏,被破坏过的线段不能被破坏(强制在线)。m次询问完后统一输出每条线段分别是在第多少次操作后被破坏的,没有被破坏的输出0。

思路:

先按照左端点排序,然后直接针对右端点建立线段树即可。因为我们可以二分找到我们每次需要查询的l,r在l,r这个区间内,直接查询有多少v大于x 并且把v更新为-inf即可,每个结点保存右端点最大值。统计答案的时候可以在更新答案的时候一并统计。

代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int mod=998244353;
const int maxm=500005;
typedef pair <int ,int >P;
struct nn
{
    int l,r,id;
    bool friend operator <(nn a,nn b)
    {
        if(a.l==b.l)
            return a.r<b.r;
        return a.l<b.l;
    }
}a[200005];
ll res,num;
int ans[200005];
vector <int >v;
#define MAXN 200010
struct node {
	int l,r;
	int v,id;
}tree[MAXN<<4];
void pushup(int rt)
{
    tree[rt].v=max(tree[rt<<1].v,tree[rt<<1|1].v);
    if(tree[rt<<1].v>=tree[rt<<1|1].v)tree[rt].id=tree[rt<<1].id;
    else tree[rt].id=tree[rt<<1|1].id;
}
void build(int l, int r,int rt)
{
    tree[rt].l=l,tree[rt].r=r;
	if (l==r){
		tree[rt].v=a[l].r;
		tree[rt].id=a[l].id;
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	pushup(rt);
}
void update(int l, int r, int d,int t,int rt)
{
	if(l>r)return ;
	if(tree[rt].v<d)return ;
	if(tree[rt].l==tree[rt].r)
    {
        num++;//破坏个数增加
        tree[rt].v=-inf;//更新为-inf
        ans[tree[rt].id]=t;
        if(res==0)res=1;
        res=res*tree[rt].id%mod;//维护res
        return ;
    }
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if (l<=mid)update(l,r,d,t,rt<<1);
	if (r>mid)update(l,r,d,t,rt<<1|1);
	pushup(rt);
}
int main()
{
    int ca;
    scanf("%d",&ca);
    for(int jj=1;jj<=ca;jj++)
    {
        v.clear();
        memset(ans,0,sizeof ans);
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i,v.push_back(a[i].l);
        sort(a+1,a+n+1);sort(v.begin(),v.end());
        int tt=1;res=0;
        build(1,n,1);
         printf("Case #%d:\n",jj);
        while(m--)
        {
            int x;
            scanf("%d",&x);
            x=x^res;
            int k=upper_bound(v.begin(),v.end(),x)-v.begin();//找到第一个大于x的左端点的下标
            num=0,res=0;
            update(1,k,x,tt++,1);
            printf("%d\n",num);
        }
        for(int i=1;i<=n;i++)
            printf("%d%c",ans[i],i==n?'\n':' ');
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值