这是一篇单 log 的做法,总用时仅有 2.57s,远超 rk2,希望大家能 hack 或者给出严谨的证明。
思路
首先有一个思路,我们以某种顺序来考虑每一个玩具,找出能搬运它的所有机器人,选择当前搬运次数最少的来搬运,最后答案就是搬运次数最多的机器人的搬运次数。
那现在我们的问题就是以什么顺序来考虑每一个玩具,怎么找到每个玩具被哪些机器人搬运,等等。
首先,我们来想想怎么找当前哪个玩具应该被放到哪里。
显然是找一堆数中,最小值的位置。乍一看似乎很难做,因为能覆盖某个玩具的机器人,似乎是很多个不连续的点。
我们将每个玩具放到二维平面上,发现每一种机器人的限制,本质上就是对左边一片区域进行覆盖,或者对下面一片区域进行覆盖。
对每种机器人按从小到大排序后,发现能够覆盖某个玩具的机器人是连续的,一直到结尾的一段。于是,前面的问题变成了区间找最小值位置,这个可以使用线段树简单实现。
与此同时,我们还可以求出每个玩具被覆盖的次数。
可以想到一个很显然的贪心,按照被覆盖次数从少到多的次数来进行放置,为什么?
设当前要放的玩具是 A 和 B,A 可以放在 a 和 b,B 可以放在 b 。
从多到少放,先放 A,假如你放在了 b 上,那么你后面放 B 的时候,就只能放在
b 上,b 就被放了 2 次。 显然,在实际上, A 可以放在 a,B 放在
b,每个只被放一次。
有人会说了:那我一开始把 A 放在 a 不就好了。
但在实际实现中,你是很难去考虑后面的没被放置的东西的情况的。
因此从少的往多的放肯定是不劣的。
这样就好了吗?并没有,因为我们有两种机器人,考虑这么一种情况。
a 玩具可以放在 A1 上。
b 玩具可以放在 A3,B1,B2 上。
c 玩具可以放在 A2,A3,B1 上。 d 玩具可以放在 A1,A2,A3 上。
我们就按 a,b,c,d
的顺序放,有多个能放的位置取最前面那个,放置过程如下:
a 放在 A1 上,b 放在 A3 上,C 放在 A2 上,d 放在 A1 上。
我们发现,A1 被放了 2 次,实际上,我们只需要按照 a,d,c,b 的顺序,以相同的策略放置,即可做到最多的只被放置一次。
为什么会出现这种问题呢?其实和上面差不多,怎么解决呢?我们假设有一种最优的放置方案,我们只需要让后面放置的,能弥补前面放置的不优的地方即可。
我们可以按照被两种机器人总覆盖次数为第一关键字,被第一种机器人覆盖次数为第二关键字来排序,并且每次取得时候,优先取第一种机器人中能取的最前面的,再去取第二种机器人中最前面能取的。
这样就好了。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
const int mod1=1e9+7;
const int mod2=998244353;
const int inf_int=0x7f7f7f7f;
const long long inf_long=0x7f7f7f7f7f7f7f7f;
const double eps=1e-9;
char Buf[1<<23],*P1=Buf,*P2=Buf;
#define getchar() (P1==P2&&(P2=(P1=Buf)+fread(Buf,1,1<<23,stdin),P1==P2)?EOF:*P1++)
template<typename type>
inline void read(type &x){
x=0;
bool f=false;
char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
if(f) x=-x;
}
template<typename type,typename... args>
inline void read(type &x,args&... y){
read(x),read(y...);
}
bool awa;
int A,B,T,x[MAXN],y[MAXN],p[MAXN];
struct node{
int w,s,cnt,x,y;
}s[MAXN];
struct node2{
int minval,minpos;
}tree[MAXN<<2];
node2 merge(node2 n1,node2 n2){
node2 res=n1;
if(n1.minval<=n2.minval){
res.minval=n1.minval;
res.minpos=n1.minpos;
}
else{
res.minval=n2.minval;
res.minpos=n2.minpos;
}
return res;
}
void pushup(int id){
tree[id]=merge(tree[id*2],tree[id*2+1]);
}
void build(int id,int l,int r){
tree[id].minval=0;
if(l==r){
tree[id].minpos=l;
return;
}
int mid=(l+r)>>1;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
pushup(id);
}
void update(int id,int l,int r,int pos){
if(l==r){
tree[id].minval++;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) update(id*2,l,mid,pos);
else update(id*2+1,mid+1,r,pos);
pushup(id);
}
node2 query(int id,int l,int r,int L,int R){
node2 res;
res.minval=1e9,res.minpos=-1;
if(r<L||R<l) return res;
if(L<=l&&r<=R) return tree[id];
int mid=(l+r)>>1;
if(L<=mid) res=merge(res,query(id*2,l,mid,L,R));
if(R>mid) res=merge(res,query(id*2+1,mid+1,r,L,R));
return res;
}
bool cmp(node n1,node n2){
if(n1.cnt!=n2.cnt) return n1.cnt<n2.cnt;
if(n1.x!=n2.x) return n1.x>n2.x;
return n1.y>n2.y;
}
bool qwq;
signed main(){
// freopen("clear.in","r",stdin);
// freopen("clear.out","w",stdout);
// cout<<(&awa-&qwq)/1024.0/1024.0<<endl;
read(A,B,T);
for(int i=1;i<=A;i++) read(x[i]);
for(int i=1;i<=B;i++) read(y[i]);
for(int i=1;i<=T;i++) read(s[i].w,s[i].s);
sort(x+1,x+A+1);sort(y+1,y+B+1);
for(int i=1;i<=T;i++){
//注意是严格偏序。
s[i].x=upper_bound(x+1,x+A+1,s[i].w)-x;
s[i].cnt+=A-s[i].x+1;
s[i].y=upper_bound(y+1,y+B+1,s[i].s)-y;
s[i].cnt+=B-s[i].y+1;
}
sort(s+1,s+T+1,cmp);
if(s[1].cnt==0){
cout<<-1;
return 0;
}
//这里把第一种机器人和第二种放在一起处理了,能方便地选择放置位置。
build(1,1,A+B);
int ans=0;
for(int i=1;i<=T;i++){
node2 now=merge(query(1,1,A+B,s[i].x,A),query(1,1,A+B,s[i].y+A,A+B));
ans=max(ans,now.minval+1);
update(1,1,A+B,now.minpos);
}
cout<<ans;
return 0;
}