版权声明:本文为博主原创文章,禁止所有形式的未经博主允许的转载行为。 https://blog.csdn.net/qq_33330876/article/details/81503336
题目大意
有 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;
}