LOADING

加载过慢请开启缓存 浏览器默认开启

P5894 题解

这是一篇单 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;
}