AT_abc331_e [ABC331E] Set Meal 题解

题意

给定$L$个二元组$(c_i,d_i)$,再从$n$个数字$a_i$和$m$个数字$b_i$各选取一个数组成一个二元组$(a_i,b_j)$,使得二数之和最大并且满足$(i,j)$不是$L$个元组的子集。

数据范围

  • $ 1\ \leq\ N,\ M\ \leq\ 10^5 $
  • $ 0\ \leq\ L\ \leq\ \min(10^5,\ N\ M\ -\ 1) $

思路

先考虑没有$L$个二元组限制时我们是如何处理的,显然,先把求出$b$数组中的最大值再在$a$数组枚举答案。时间复杂度为$O(n)$。

现在考虑有条件限制的情况,很容易发现对于一个$i$,把它不能与它配对的$j$排序后的数组为$g$,那么对于所有$x$都可以在$g[k]+1 \sim\ g[k+1]-1$内与$i$配对。根据最优性,我们需要在这个区间内以$O(logn)$的时间复杂度求出最大值,此时就可用$ST$表或者线段树维护。时间复杂度$O(mlogm+nlogn)$。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+50;
int f[N][19],a[N],b[N],n,m,l,ans,LG[N];
vector<int> g[N];
void st_prework(){
    for(int j=1;j<=log2(m);j++){
        for(int i=1;i+(1<<j)-1<=m;i++){
            f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
        }
    }
    return;
}
int query(int l,int r){
    if(r<l)return 0;
    int k=log2(r-l+1);
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
signed main(){
    scanf("%lld %lld %lld",&n,&m,&l);
    LG[0]=-1;
    for(int i=1;i<=max(n,m);i++)LG[i]=LG[i>>1]+1;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=m;i++){
        scanf("%lld",&b[i]);
        f[i][0]=b[i];
    }
    for(int i=1;i<=l;i++){
        int x,y;
        scanf("%lld %lld",&x,&y);
        g[x].push_back(y);
    }
    st_prework();
    for(int i=1;i<=n;i++){
        int res=0;
        if(g[i].size()==0){
            res=query(1,m);
        }else{
            sort(g[i].begin(),g[i].end());
            int len=g[i].size();
            res=max(res,query(1,g[i][0]-1));
            for(int j=0;j<len-1;j++){
                res=max(res,query(g[i][j]+1,g[i][j+1]-1));
            }
            res=max(res,query(g[i][len-1]+1,m));
        }
        ans=max(ans,res+a[i]);
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2023-12-04 20:05  _apologize  阅读(26)  评论(0)    收藏  举报  来源