[APIO2018] New Home 新家(线段树,二分答案,离散化)

[APIO2018] New Home 新家

Solution

对于时间轴我们直接离散化+扫描线,维护每一个商店的加入和删除。

对于询问 ( x , t ) (x,t) (x,t),不好直接回答,这里的关键一步是:我们要求的是 k k k种商店最小距离的最大值,于是考虑二分答案。二分一个最大值 m i d mid mid,那么 k k k种商店必须都在 [ x − m i d , x + m i d ] [x-mid,x+mid] [xmid,x+mid]出现过。我们考虑对于每一个商店维护和它类型相同的商店的前驱。那么 [ x − m i d , x + m i d ] [x-mid,x+mid] [xmid,x+mid]的条件就转化为了 ( x + m i d , ∞ ) (x+mid,\infty) (x+mid,)中的商店的前驱都不小于 x − m i d x-mid xmid(注意 − 1 -1 1的特殊情况)。

于是我们用一个 s e t set set维护每种商店的序列,用线段树维护后缀最小值即可。

时间复杂度 O ( n l g n ⋅ l g T ) O(nlgn\cdot lgT) O(nlgnlgT)

Details

具体实现时有一些细节:

  • 我们把每一种商店的第一个的前驱设为 0 0 0
  • 每种商店的最后加一个 ∞ \infty 以简化 − 1 -1 1的特判,具体可见代码。
  • 可能会出现类型坐标时间都相同的商店,因此我们要在线段树每个位置维护一个 m u l t i s e t multiset multiset表示该坐标的最小值,而不能直接修改一个坐标的最小值。维护每个类型的 s e t set set也要是 m u l t i s e t multiset multiset
  • 因为怕麻烦,我直接用的是动态开点线段树,跑得也挺轻松的。

Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>

#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second

using namespace std;

template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }

typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;

const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=600005;
const int MX=1e8+300000;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
	while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x*f;
}
multiset<int> V[MAXN],Set[MAXN];
int nodenum=1,mn[MAXN*30],ls[MAXN*30],rs[MAXN*30],Ans[MAXN],id[MAXN*30],ID=0;
struct Cnode{ int t,x,y; } C[MAXN<<1],Q[MAXN];
void up(int x)
{
	mn[x]=INF;
	if (ls[x]) upmin(mn[x],mn[ls[x]]);
	if (rs[x]) upmin(mn[x],mn[rs[x]]);
}
int query(int x,int l,int r,int L,int R)
{
	if (!x) return INF;
	if (l>=L&&r<=R) return mn[x];
	int mid=(l+r)>>1;
	if (R<=mid) return query(ls[x],l,mid,L,R);
	else if (L>mid) return query(rs[x],mid+1,r,L,R);
	else return min(query(ls[x],l,mid,L,mid),query(rs[x],mid+1,r,mid+1,R));
}
int update(int x,int l,int r,int y,int z)
{
	if (!x) x=++nodenum;
	if (l==r) { id[x]=(id[x]?id[x]:++ID),V[id[x]].insert(z),mn[x]=*V[id[x]].begin(); return x; }
	int mid=(l+r)>>1;
	if (y<=mid) ls[x]=update(ls[x],l,mid,y,z);
	else rs[x]=update(rs[x],mid+1,r,y,z);
	up(x);
	return x;
}
void clear(int x,int l,int r,int y,int z)
{
	if (l==r) { V[id[x]].erase(V[id[x]].find(z)); mn[x]=(V[id[x]].size()?*V[id[x]].begin():INF); return; }
	int mid=(l+r)>>1;
	if (y<=mid) clear(ls[x],l,mid,y,z);
	else clear(rs[x],mid+1,r,y,z);
	up(x);
}

int check(int x,int l) { return query(1,1,MX,min(x,(int)1e8+1),MX)>=max(l,1);  }
signed main()
{
	int n=read(),k=read(),q=read(),num=0;
	for (int i=1;i<=n;i++)
	{
		int x=read(),y=read(),a=read(),b=read();
		C[++num]=(Cnode){a,x,y},C[++num]=(Cnode){b+1,x,-y};
	}
	for (int i=1,x,y;i<=q;i++) x=read(),y=read(),Q[i]=(Cnode){y,x,i};
	sort(C+1,C+num+1,[&](Cnode a,Cnode b){ return (a.t<b.t)||(a.t==b.t&&a.x>b.x); });
	sort(Q+1,Q+q+1,[&](Cnode a,Cnode b){ return a.t<b.t; });
	for (int i=1;i<=k;i++) Set[i].insert(1e8+i),update(1,1,MX,1e8+i,0);
	C[num+1].t=INF;

	int nw=1;
	while (nw<=q&&Q[nw].t<C[1].t) Ans[Q[nw].y]=-1,nw++;
	for (int i=1;i<=num;i++)
	{
		if (C[i].y>0)
		{
			int t=C[i].y;
			Set[t].insert(C[i].x);
			multiset<int>::iterator it=Set[t].find(C[i].x),pre=it,nxt=it;
			update(1,1,MX,*(++nxt),*it);
			if (pre!=Set[t].begin()) update(1,1,MX,*it,*(--pre)),clear(1,1,MX,*(nxt),*pre);
			else update(1,1,MX,*it,0),clear(1,1,MX,*nxt,0);
		}
		else if (C[i].y<0)
		{
			int t=-C[i].y;
			multiset<int>::iterator it=Set[t].find(C[i].x),pre=it,nxt=it;
			clear(1,1,MX,*(++nxt),*it);
			if (pre!=Set[t].begin()) clear(1,1,MX,*it,*(--pre)),update(1,1,MX,*nxt,*pre);
			else clear(1,1,MX,*it,0),update(1,1,MX,*nxt,0);
			Set[t].erase(it);
		}
		while (nw<=q&&Q[nw].t<C[i+1].t)
		{
			int t=Q[nw].x,l=0,r=1e8;
			while (l<r)
			{
				int mid=(l+r)>>1;
				if (check(t+mid+1,t-mid)) r=mid;
				else l=mid+1;
			}
			Ans[Q[nw].y]=(!check(t+r+1,t-r)?-1:r);
			nw++;
		}
	}
	for (int i=1;i<=q;i++) printf("%d\n",Ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值