x 是看题解的。

[] CF1055E

题意:给定 $n$ 个点,以及 $m$ 条线段,选择 $s$ 条线段,使得至少被一个线段覆盖的点的坐标从小到大排序后,第 $k$ 大最小,没有则输出 $-1$ 。

二分答案 $k$ ,则现在问题变为了选择 $k$ 个线段使得至少覆盖 $p$ 个点。

按照 $r$ 排序后考虑 $dp$,设 $f_i$ 表示选择 $i$ 的最多点数。

由于直接转移是 $O(m^3)$ 通不过本题,则对于相交与不相交建立线段树维护即可。

时间复杂度 $O(m^2\log^2n)$ 。

貌似可以对于点 $dp$ ,维护每个点最右连续 $1$ 位置。$O(1)$ 转移即可,时间复杂度 $O(m^2\log n)$ 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<climits>
#include<set>
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=1501;
struct Segment{
    int Maxn[MAXN<<2],inf;
    void clear(){memset(Maxn,-127/3,sizeof(Maxn));inf=Maxn[0];return;}
    void Modify(int k,int l,int r,int ps,int w){
        if(l==r){Maxn[k]=w;return;}
        int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps,w); if(mid<ps) Modify(k<<1|1,mid+1,r,ps,w);
        Maxn[k]=max(Maxn[k<<1],Maxn[k<<1|1]); return;
    }
    int Query(int k,int l,int r,int x,int y){
        if(x<=l&&r<=y) return Maxn[k];
        int res=inf,mid=(l+r)>>1; if(x<=mid) res=max(res,Query(k<<1,l,mid,x,y)); if(mid<y) res=max(res,Query(k<<1|1,mid+1,r,x,y));
        return res;
    }
}S[MAXN];
int N,M,K,kth,A[MAXN],tmp[MAXN],cnt,SS[MAXN];
struct Node{int l,r,len;} F[MAXN],G[MAXN];
int f[MAXN][MAXN],pre[MAXN],Maxn[MAXN][MAXN];
bool cmp(Node x1,Node x2){return x1.len<x2.len;}
bool cmp1(Node x1,Node x2){return x1.r<x2.r;}
bool check(int xx){
    for(int i=1;i<=N;i++) SS[i]=SS[i-1]+(A[i]<=xx);
    for(int i=0;i<=K;i++) S[i].clear();
    memset(f,-127/3,sizeof(f)); memset(Maxn,-127/3,sizeof(Maxn));f[0][0]=0; S[0].Modify(1,0,cnt,0,0);
    for(int i=0;i<=cnt;i++) Maxn[0][i]=0;
    int Max=0;
    for(int i=1;i<=K;i++){
        for(int j=1;j<=cnt;j++){
            f[i][j]=max(f[i][j],Maxn[i-1][pre[j]]+SS[G[j].r]-SS[G[j].l-1]);
            if(pre[j]+1<=j-1){    
                int W=S[i-1].Query(1,0,cnt,pre[j]+1,j-1)+SS[G[j].r];
                f[i][j]=max(f[i][j],W);    
            }
            Maxn[i][j]=max(f[i][j],Maxn[i][j-1]);
            Max=max(Max,f[i][j]);
            S[i].Modify(1,0,cnt,j,f[i][j]-SS[G[j].r]);
        }
    }
    return Max>=kth;
}
int main(){
    //freopen("A.in","r",stdin);
    N=read(),M=read(),K=read(),kth=read();
    for(int i=1;i<=N;i++) A[i]=tmp[i]=read(); sort(tmp+1,tmp+N+1);
    int E=unique(tmp+1,tmp+N+1)-tmp-1;
    for(int i=1;i<=N;i++) A[i]=lower_bound(tmp+1,tmp+E+1,A[i])-tmp;
    for(int i=1;i<=M;i++) F[i].l=read(),F[i].r=read(),F[i].len=F[i].r-F[i].l+1; sort(F+1,F+M+1,cmp);
    for(int i=1;i<=M;i++){
        bool ff=1; for(int j=i+1;j<=M;j++) if(F[j].l<=F[i].l&&F[i].r<=F[j].r) {ff=0;break;}
        if(ff) G[++cnt]=F[i];
    }sort(G+1,G+cnt+1,cmp1); int l=1,r=E,res=-1;
    for(int i=1;i<=cnt;i++) for(int j=0;j<=i-1;j++) if(G[j].r<G[i].l) pre[i]=j;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)) res=mid,r=mid-1;
        else l=mid+1;
    }if(res==-1) printf("-1\n"); else printf("%d\n",tmp[res]);
    return 0;
}/*
4 3 2 2
3 1 3 2
1 2
2 3
4 4
*/
View Code

相关文章:

  • 2021-05-29
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2023-01-14
  • 2021-07-19
  • 2021-08-27
  • 2022-02-18
猜你喜欢
  • 2021-09-13
  • 2021-05-25
  • 2018-05-17
  • 2021-09-24
  • 2022-02-11
  • 2021-06-17
  • 2022-12-23
相关资源
相似解决方案