【题目】
原题地址
题目大意:太长了去看题面吧。
【题目分析】
一道看上去比较奇怪的题目,需要一定转化思想。
不过二分答案这个点还是比较显然的。
【解题思路】
对时间扫描的话,每间商店等价于插入操作和删除操作。
问题转化为支持插入/删除,询问以某个位置为重心包含所有不同数字的最小长度。
对于询问,显然我们可以二分答案。但是如何查询一个区间内是否出现所有种类的数?
考虑出现的充要条件,以及出现的数与前后的关联。
可以发现,若一个数 x x 在 [l,r] [ l , r ] 中出现,且在 [r+1,+oo] [ r + 1 , + o o ] 中出现,对于 [r+1,+oo] [ r + 1 , + o o ] 中的每一个 x x ,它的上一次出现位置必然 >=l >= l 。
因此我们可以维护一个平衡树来支持前驱后继的查找。
对于每一个时间位置,我们要维护的就是每个数字上一次出现的位置,并取这些位置中的最小值作为这个位置的权值。
这样我们就可以通过查询 [r+1,+oo] [ r + 1 , + o o ] 中的最小值来判断了。(当然在末尾需要多放每个数字)
如果我们开一棵全局线段树,对于序列的每个位置再维护一个堆,就可以用 O(nlog2n) O ( n l o g 2 n ) 的复杂度完成这道题。
但是这里发现这个堆是不必要的,因为对于同一编号的商店各个商店位置不同(如果相同的话看时间维,时间维有交可合并,不交不影响,其实交也不影响),我们完全可以只用一棵线段树,记录当前下标的 pre p r e ,并用 set s e t 维护即可,时间复杂度还是 O(nlog2n) O ( n l o g 2 n )
这里有一种一个 log的做法 l o g 的 做 法 ,解法在这,直接贴了:
考虑对每个询问二分答案
如果询问坐标为 x x ,答案 ≥m ≥ m 等价于存在一种店在 (x−m,x+m) ( x − m , x + m ) 不存在店,等价于在 [x+m,inf) [ x + m , i n f ) 存在店前驱 ≤x−m ≤ x − m 。
对每种店开个 set s e t ,对全局开个线段树维护区间最小前驱即可两个 log l o g 。
在线段树上二分即可一个 log l o g 。
具体说一下。
你要求的是最大的 i i ,满足 记 [i,inf) [ i , i n f ) 的最小前驱为 mn m n ,则 mn+i≤2×x m n + i ≤ 2 × x 。
无解的情况先特判掉。
假如你现在在线段树上的 [l,r] [ l , r ] ,
如果 x x 落在 [mid+1,r] [ m i d + 1 , r ] ,那么 i i 一定落在 [mid+1,r] [ m i d + 1 , r ] 。
如果 x x 落在 [1,mid] [ 1 , m i d ] ,那么你需要判断最终的答案会不会落在 [mid+1,r] [ m i d + 1 , r ] 上。
由于 i i 越小, mn m n 越小,所以你只用检查一下 mid+1 m i d + 1 是否满足 mn+i≤2×x m n + i ≤ 2 × x 即可。
【参考代码】
#include
using namespace std;
const int N=1e6+10;
const int INF=1e9+10;
int n,m,qs,cnt,cn;
int ans[N];
set<int>mp[N];
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){ret=ret*10+(c^48);c=getchar();}
return f?ret:-ret;
}
void write(int x)
{
if(x<0)x=-x,putchar('-');
if(x>9)write(x/10);
putchar(x%10^48);
}
struct Tshop
{
int x,t,l,r;
};
Tshop a[N];
bool cmp(Tshop A,Tshop B)
{
return A.xx;
}
struct Tquery
{
int op,pos,x,t;
Tquery(){}
Tquery(int opt,int pp,int xx,int tt){
op=opt;pos=pp;x=xx;t=tt;
}
};
Tquery q[N];
bool cmpq(Tquery A,Tquery B)
{
if(A.t^B.t)
return A.treturn A.opint mi[N<<3];
void pushup(int x)
{
mi[x]=min(mi[x<<1],mi[x<<1|1]);
}
void build(int x,int l,int r)
{
if(l==r)
{
mi[x]=(l<=n)?INF:0;
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);build(x<<1|1,mid+1,r);
pushup(x);
}
void update(int x,int l,int r,int p,int val)
{
if(l==r)
{
mi[x]=val;
return;
}
int mid=(l+r)>>1;
if(p<=mid)
update(x<<1,l,mid,p,val);
else
update(x<<1|1,mid+1,r,p,val);
pushup(x);
}
int query(int x,int l,int r,int p)
{
if(a[l].x>p)
return mi[x];
int mid=(l+r)>>1,ret=INF;
if(a[mid].x>p)
ret=min(ret,query(x<<1,l,mid,p));
ret=min(ret,query(x<<1|1,mid+1,r,p));
return ret;
}
}tr;
void init()
{
n=read();m=read();qs=read();
for(int i=1;i<=n;++i)
{
a[i].x=read();a[i].t=read();
a[i].l=read();a[i].r=read();
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;++i)
{
q[++cnt]=Tquery(1,0,i,a[i].l);
q[++cnt]=Tquery(3,0,i,a[i].r);
}
for(int i=1;i<=qs;++i)
{
int x=read(),t=read();
q[++cnt]=Tquery(2,i,x,t);
}
sort(q+1,q+cnt+1,cmpq);
//for(int i=1;i<=n;++i)
//printf("%d %d %d %d\n",a[i].x,a[i].t,a[i].l,a[i].r);
//for(int i=1;i<=cnt;++i)
//printf("%d %d %d %d\n",q[i].op,q[i].pos,q[i].x,q[i].t);
for(int i=1;i<=m;++i)
{
mp[i].insert(0);mp[i].insert(i+n);
a[i+n].x=INF;
}
}
void solve()
{
cn=n+m;tr.build(1,1,cn);
for(int i=1;i<=cnt;++i)
{
if(q[i].op==1)
{
int x=q[i].x;mp[a[x].t].insert(x);
set<int>::iterator lp=mp[a[x].t].lower_bound(x),rp=mp[a[x].t].upper_bound(x);
if(lp!=mp[a[x].t].begin())
--lp;
//printf("%d %d\n",*lp,*rp);
tr.update(1,1,cn,x,*lp);tr.update(1,1,cn,*rp,x);
}
else
if(q[i].op==3)
{
int x=q[i].x;mp[a[x].t].erase(x);
set<int>::iterator lp=mp[a[x].t].lower_bound(x),rp=mp[a[x].t].upper_bound(x);
if(lp!=mp[a[x].t].begin())
--lp;
//printf("%d %d\n",*lp,*rp);
tr.update(1,1,cn,x,INF);tr.update(1,1,cn,*rp,*lp);
}
else
{
int x=q[i].x,l=0,r=INF,ret=INF;
while(lint mid=(l+r)>>1;
//printf("%d\n",mid);
int fl=max(1,x-mid),fr=min(INF-1,x+mid);
if(a[tr.query(1,1,cn,fr)].x>=fl)
r=mid,ret=mid;
else
l=mid+1;
}
//printf("ret:%d\n",ret);
ans[q[i].pos]=(ret==INF)?-1:ret;
}
}
for(int i=1;i<=qs;++i)
write(ans[i]),puts("");
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("LOJ2585.in","r",stdin);
freopen("LOJ2585.out","w",stdout);
#endif
init();
solve();
return 0;
}
【总结】
平衡树维护前驱,以及线段树分治的思想都很优秀,这道题目有很多值得借鉴的思想,是一道很好的题目qwq。