牛客网暑期ACM多校训练营(第六场)-- I-Team Rocket
程序员文章站
2022-06-01 21:02:14
...
题意:
有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;
}
上一篇: LitePal操作数据库详解