Luogu6349 [PA2011]Kangaroos
Luogu6349 [PA2011]Kangaroos
\(\rm KDT\),历史最值
区间\([l_1,r_1]\)和区间\([l_2,r_2]\)相交,当且仅当\(l_1 \le r_2 \operatorname{and} l_2 \le r_1\),这就把题目转化为了一个二维平面上的修改和询问的问题。
考虑把询问序列当成二维平面上的点,依次加入序列\(a\),实时维护当前的答案。也就是,对满足条件的,对应点的值\(+1\),其他位置的值清\(0\),然后维护历史最大值即为答案。
然后,……,这个维护就离谱!\(WA\)了半天才通过。
如果不管历史最值,那么显然是维护加法标记和清空标记,钦定先清空后添加。
维护历史最值类似线段树维护,注意一种情况,就是在一个节点先清空,再加,再清空,实际上相当于一个值在这个节点短暂停留,还没有下放就消失了,我们不能忽略这一部分的贡献,可以维护一个标记永久化,最终处理时整棵子树答案与之取\(\max\)。同时也要注意清空时,加法标记和历史最大加法标记的下放。具体细节详见代码。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<assert.h>
#define N 50005
#define M 200005
using namespace std;
const int INF=1000000007;
int n,m,mxp,ans[M];
struct Point
{
int x,y,id;
Point () {}
Point (int xx,int yy,int zz):x(xx),y(yy),id(zz) {}
void read(int I)
{
scanf("%d%d",&x,&y);
id=I;
}
}a[N],b[M];
struct node
{
int xl,xr,yl,yr;
int s,mxs,atag,mxatag,mxas;
bool ctag;
}tr[M << 2];
struct range_query
{
int xl,xr,yl,yr;
}kz;
bool cmpX(const Point &A,const Point &B)
{
return A.x<B.x;
}
bool cmpY(const Point &A,const Point &B)
{
return A.y<B.y;
}
void build(int p,int l,int r,bool opt)
{
mxp=max(mxp,p);
if (l==r)
{
tr[p].xl=tr[p].xr=b[l].x;
tr[p].yl=tr[p].yr=b[l].y;
return;
}
int mid(l+r >> 1);
nth_element(b+l,b+mid,b+r+1,(!opt)?cmpX:cmpY);
build(p << 1,l,mid,!opt),build(p << 1 | 1,mid+1,r,!opt);
tr[p].xl=min(tr[p << 1].xl,tr[p << 1 | 1].xl);
tr[p].xr=max(tr[p << 1].xr,tr[p << 1 | 1].xr);
tr[p].yl=min(tr[p << 1].yl,tr[p << 1 | 1].yl);
tr[p].yr=max(tr[p << 1].yr,tr[p << 1 | 1].yr);
}
void push_tag(int p,int tag1,int tag2)
{
if (p>mxp)
return;
assert(tag1<=tag2);
tr[p].mxs=max(tr[p].mxs,tr[p].s+tag2);
tr[p].mxatag=max(tr[p].mxatag,tr[p].atag+tag2);
tr[p].s+=tag1,tr[p].atag+=tag1;
}
void push_clean(int p)
{
if (p>mxp)
return;
if (tr[p].ctag)
{
tr[p].mxas=max(tr[p].mxas,tr[p].mxatag);
tr[p].atag=tr[p].mxatag=tr[p].s=0;
tr[p].ctag=true;
return;
}
if ((p << 1)<=mxp && tr[p << 1].ctag)
tr[p << 1].mxatag=max(tr[p << 1].mxatag,tr[p << 1].atag+tr[p].mxatag),tr[p << 1].mxas=max(tr[p << 1].mxas,tr[p << 1].mxatag),tr[p << 1].atag=tr[p << 1].mxatag=0; else
push_tag(p << 1,tr[p].atag,tr[p].mxatag);
if ((p << 1 | 1)<=mxp && tr[p << 1 | 1].ctag)
tr[p << 1 | 1].mxatag=max(tr[p << 1 | 1].mxatag,tr[p << 1 | 1].atag+tr[p].mxatag),tr[p << 1 | 1].mxas=max(tr[p << 1 | 1].mxas,tr[p << 1 | 1].mxatag),tr[p << 1 | 1].atag=tr[p << 1 | 1].mxatag=0; else
push_tag(p << 1 | 1,tr[p].atag,tr[p].mxatag);
tr[p].atag=tr[p].mxatag=tr[p].s=0;
tr[p].ctag=true;
}
void push_down(int p)
{
if (tr[p].ctag)
{
push_clean(p << 1);
push_clean(p << 1 | 1);
}
push_tag(p << 1,tr[p].atag,tr[p].mxatag);
push_tag(p << 1 | 1,tr[p].atag,tr[p].mxatag);
tr[p].atag=tr[p].mxatag=0;
tr[p].ctag=false;
}
void calc(int p)
{
push_down(p);
if (tr[p].xl>kz.xr || tr[p].xr<kz.xl || tr[p].yl>kz.yr || tr[p].yr<kz.yl)
{
push_clean(p);
return;
}
if (kz.xl<=tr[p].xl && tr[p].xr<=kz.xr && kz.yl<=tr[p].yl && tr[p].yr<=kz.yr)
{
push_tag(p,1,1);
return;
}
calc(p << 1),calc(p << 1 | 1);
}
void calc_all(int p,int l,int r)
{
push_down(p);
if (l==r)
{
ans[b[l].id]=max(tr[p].mxs,tr[p].mxas);
return;
}
int mid(l+r >> 1);
tr[p << 1].mxas=max(tr[p << 1].mxas,tr[p].mxas),tr[p << 1 | 1].mxas=max(tr[p << 1 | 1].mxas,tr[p].mxas);
calc_all(p << 1,l,mid),calc_all(p << 1 | 1,mid+1,r);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
a[i].read(i);
for (int i=1;i<=m;++i)
b[i].read(i);
build(1,1,m,0);
for (int i=1;i<=n;++i)
{
kz.xl=1,kz.xr=a[i].y;
kz.yl=a[i].x,kz.yr=INF;
calc(1);
}
calc_all(1,1,m);
for (int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}