专栏文章
题解:P9061 [Ynoi2002] Optimal Ordered Problem Solver
P9061题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @min5vkf5
- 此快照首次捕获于
- 2025/12/01 21:04 3 个月前
- 此快照最后确认于
- 2025/12/01 21:04 3 个月前
思路
首先发现,被操作过的点一定会形成一段轮廓线,且随着 x 坐标的增加,y 坐标单调不升。如下图所示。

还剩下一些散点在轮廓线之外,我们把这两部分分开做。
首先对于轮廓线上的点,使用 FHQ-Treap 维护每个点的坐标 。修改操作一定是修改一段区间,把这段区间分裂出来,打上对 x 坐标或者 y 坐标覆盖标记。
然后修改的时候会有一些点加入轮廓线,显然每个点最多加入一次,所以直接将这些点修改后的坐标插入平衡树即可。
查询的时候轮廓线上的点也是一段区间,把这段区间求出来即可。
对于轮廓线外的点,设第 个点第一次被修改到轮廓线上的时间为 ,这个东西类似于二维偏序,离线扫描线即可。
那么对当前第 次操作之后有贡献当且仅当 ,这是一个三维偏序的形式,可以离线做 CDQ 分治。复杂度为 。
然而这显然不够优秀,考虑优化。
首先查询的点在轮廓线的下方答案就是 ,可以忽略。
否则考虑计算不合法的个数,这个相当于 并上 的个数。
使用容斥拆分为 的个数加上 的个数减去 的个数。由于在满足 时一定满足 ,所以就是三个二维数点,也可以用离线扫描线解决。
时间复杂度 ,但是得注意常数。
卡常
首先一定要使用快读。
扫描线中,不能使用 vector 存储挂在当前扫到的数下的点,正确的做法是把所有点存到一个数组里然后排序,扫描线中使用双指针求这些点。
每一次修改会插入一些点,若每次都分裂一次再合并,也是不优的。更优秀的做法是先全部分裂开,然后插入这些点后再合并。不过这一点不是特别需要。
查询时不能将这个区间分裂出来然后输出这颗树的大小,然后把分裂出来的树合并。正确的做法是不分裂,直接求。
具体的,由于 小的是一段前缀, 小的是一段后缀,现在要求交集的大小,可以单独求出这个前缀和后缀,然后容斥求出答案。
代码
CPP#include<bits/stdc++.h>
using namespace std;
namespace IO{
const int BUF=1<<22;
char buff[BUF],*p1=buff,*p2=buff;
#define gc() (p1==p2&&(p2=((p1=buff)+fread(buff,1,BUF,stdin)),p1==p2)?EOF:*p1++)
template<typename T>inline void read(T &x){
char ch=gc();x=0;int f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=gc();
x*=f;
}
char obuf[BUF],*p3=obuf;
inline void pc(char ch){
if(p3-obuf<BUF) *p3++=ch;
else fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=ch;
}
inline void put(const char *s){while(*s) pc(*s),++s;}
char ch[32];int ctop;
template<typename T>inline void write(T x){
if(x<0) x=~x+1,pc('-');
if(!x) return pc(48);
while(x) ch[++ctop]=x%10^48,x/=10;
while(ctop) pc(ch[ctop--]);
}
inline void flush(){fwrite(obuf,p3-obuf,1,stdout);p3=obuf;}
}
using namespace IO;
const int N = 1e6+5;
int n,m;
mt19937 rnd(1145141);
struct node{
int x,y,sz,ls,rs,key,tagx,tagy;
}t[N];
int rt,x,y,z,cnt;
int make(int x,int y)
{
++cnt;
t[cnt].sz = 1;
t[cnt].tagx = t[cnt].tagy = -1;
t[cnt].x = x,t[cnt].y = y;
t[cnt].key = rnd();
return cnt;
}
inline void pushup(int k){t[k].sz = t[t[k].ls].sz+t[t[k].rs].sz+1;}
inline void coverx(int k,int v){t[k].tagx = t[k].x = v;}
inline void covery(int k,int v){t[k].tagy = t[k].y = v;}
inline void down(int k)
{
if(t[k].tagx!=-1)
{
if(t[k].ls) coverx(t[k].ls,t[k].tagx);
if(t[k].rs) coverx(t[k].rs,t[k].tagx);
t[k].tagx = -1;
}
if(t[k].tagy!=-1)
{
if(t[k].ls) covery(t[k].ls,t[k].tagy);
if(t[k].rs) covery(t[k].rs,t[k].tagy);
t[k].tagy = -1;
}
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(t[x].key<t[y].key)
{
down(x),t[x].rs = merge(t[x].rs,y),pushup(x);
return x;
}
else
{
down(y),t[y].ls = merge(x,t[y].ls),pushup(y);
return y;
}
}
void splitx(int k,int v,int &x,int &y)
{
if(!k) return x = y = 0,void();
down(k);
if(t[k].x<=v) x = k,splitx(t[k].rs,v,t[k].rs,y);
else y = k,splitx(t[k].ls,v,x,t[k].ls);
pushup(k);
}
void splity(int k,int v,int &x,int &y)
{
if(!k) return x = y = 0,void();
down(k);
if(t[k].y>=v) x = k,splity(t[k].rs,v,t[k].rs,y);
else y = k,splity(t[k].ls,v,x,t[k].ls);
pushup(k);
}
void split(int k,int v1,int v2,int &x,int &y)
{
if(!k) return x = y = 0,void();
down(k);
if(t[k].x<v1||(t[k].x==v1&&t[k].y>v2)) x = k,split(t[k].rs,v1,v2,t[k].rs,y);
else y = k,split(t[k].ls,v1,v2,x,t[k].ls);
pushup(k);
}
int askx(int k,int v)
{
if(!k) return 0;
down(k);
if(t[k].x<=v) return t[t[k].ls].sz+1+askx(t[k].rs,v);
else return askx(t[k].ls,v);
}
int asky(int k,int v)
{
if(!k) return 0;
down(k);
if(t[k].y<=v) return t[t[k].rs].sz+1+asky(t[k].ls,v);
else return asky(t[k].rs,v);
}
#define lowbit(x) (x&(-x))
namespace t1{
int t[N];
inline void add(int x,int v)
{
for(;x&&t[x]>v;x-=lowbit(x))
t[x] = v;
}
inline int ask(int x)
{
int res = m+1;
for(;x<=n;x+=lowbit(x))
res = min(res,t[x]);
return res;
}
}
namespace t2{
int t[N];
inline void add(int x,int v)
{
for(;x;x-=lowbit(x))
t[x]+=v;
}
inline int ask(int x)
{
int res = 0;
for(;x<=n;x+=lowbit(x))
res+=t[x];
return res;
}
}
int O[N],A[N],B[N],X[N],Y[N];
struct node1{
int w,x;
inline friend bool operator < (node1 x,node1 y)
{
return x.w<y.w;
}
}vec2[N],vec3[N],vec4[N];
int cnt2,cnt3,cnt4;
struct node2{
int w,x,y;
inline friend bool operator < (node2 x,node2 y)
{
return x.w<y.w;
}
}vec1[N];
int cnt1;
bool ok[N];
int ans[N];
int p[N];
pair<int,int> now[N];
inline bool cmp(pair<int,int> x,pair<int,int> y){return (x.first==y.first)?(x.second>y.second):(x.first<y.first);}
signed main()
{
read(n),read(m);
for(int i = 1,x,y;i<=n;i++)
read(x),read(y),vec2[++cnt2] = {x,y};
for(int i = 1;i<=m;i++)
{
read(O[i]),read(A[i]),read(B[i]),read(X[i]),read(Y[i]);
vec3[++cnt3] = {A[i],i},vec4[++cnt4] = {X[i],i};
}
sort(vec2+1,vec2+cnt2+1);
sort(vec3+1,vec3+cnt3+1);
sort(vec4+1,vec4+cnt4+1);
for(int i = 1;i<=n;i++) t1::t[i] = m+1;
int p2 = cnt2,p3 = cnt3,p4 = cnt4;
for(int i = n;i;i--)
{
while(p3&&vec3[p3].w>=i)
{
int j = vec3[p3].x;
t1::add(B[j],j);
p3--;
}
while(p2&&vec2[p2].w>=i)
{
int j = vec2[p2].x;
vec1[++cnt1] = {t1::ask(j),i,j};
p2--;
}
while(p4&&vec4[p4].w>=i-1)
{
int j = vec4[p4].x;
if(j>=t1::ask(Y[j]+1)) ok[j] = 1;
p4--;
}
}
sort(vec1+1,vec1+cnt1+1);
int p1 = cnt1;
for(int i = m;i;i--)
{
while(p1&&vec1[p1].w>=i+1) t2::add(vec1[p1].x,1),p1--;
ans[i]+=t2::ask(X[i]+1);
}
for(int i = 1;i<=n;i++) t2::t[i] = 0;
p1 = cnt1;
for(int i = m;i;i--)
{
while(p1&&vec1[p1].w>=i+1) t2::add(vec1[p1].y,1),p1--;
ans[i]+=t2::ask(Y[i]+1);
}
for(int i = 1;i<=n;i++) t2::t[i] = 0;
p2 = cnt2,p4 = cnt4;
for(int i = n;i;i--)
{
while(p2&&vec2[p2].w>=i+1)
{
int j = vec2[p2].x;
t2::add(j,1);
p2--;
}
while(p4&&vec4[p4].w>=i)
{
int j = vec4[p4].x;
ans[j]-=t2::ask(Y[j]+1);
p4--;
}
}
int tmp = 0;
p1 = 1;
for(int i = 1;i<=m;i++)
{
splitx(rt,A[i],x,z);
splity(x,B[i]+1,x,y);
if(y)
{
if(O[i]==2) coverx(y,A[i]);
else covery(y,B[i]);
}
rt = merge(x,merge(y,z));
int tot = 0;
while(p1<=cnt1&&vec1[p1].w<=i)
{
tmp++;
int X = vec1[p1].x,Y = vec1[p1].y;
if(O[i]==2) X = A[i];
else Y = B[i];
now[++tot] = {X,Y};
p1++;
}
sort(now+1,now+tot+1,cmp);
p[0] = rt;
for(int i = 1;i<=tot;i++)
{
split(p[i-1],now[i].first,now[i].second,p[i-1],p[i]);
p[i-1] = merge(p[i-1],make(now[i].first,now[i].second));
}
rt = 0;
for(int i = 0;i<=tot;i++)
rt = merge(rt,p[i]);
if(!ok[i]) ans[i] = n-tmp-ans[i]+max(askx(rt,X[i])+asky(rt,Y[i])-tmp,0);
}
for(int i = 1;i<=m;i++)
{
if(ok[i]) ans[i] = 0;
write(ans[i]),pc('\n');
}
flush();
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...