1. 程式人生 > >CF-1055E:Segments on the Line (二分&揹包&DP優化)(nice problem)

CF-1055E:Segments on the Line (二分&揹包&DP優化)(nice problem)

You are a given a list of integers

You need to select exactly

k">k elements, print -1.

The

Input

The first line contains four integers

s≤1500">1ms1500 1≤m≤s≤1500 ,

The second line contains

Each of the next

It is possible that some segments coincide.

Output

Print exactly one integer — the smallest possible

Examples Input
4 3 2 2
3 1 3 2
1 2
2 3
4 4
Output
2
Input
5 2 1 1
1 2 3 4 5
2 4
1 5
Output
1
Input
5 3 3 5
5 5 2 1 1
1 2
2 3
3 4
Output
-1

題意:給定給N個點,以及M個線段,讓你選擇S個線段,使得至少被一個線段覆蓋的點排序後,第K大最小,沒有則輸出-1。

思路:求第K大最小,顯然需要二分,每次驗證看當前的mid是否有大於等於K個數小於mid。驗證我們用dp來驗證,複雜度是O(NMS*lgN);

需要優化掉一個。這裡用揹包把M優化掉了,我們找到每個點的Next,Next代表包含這個點的最右端。就不難得到dp方程,這個時候M已經沒用了。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1510;
struct in{ int L,R;}s[maxn];
int a[maxn],b[maxn],N,S,M,K,sum[maxn];
int dp[maxn][maxn],Next[maxn];
bool check(int Mid) //M個選最多S個的第K大
{
    rep(i,1,N) sum[i]=sum[i-1]+(a[i]<=Mid);
    rep(i,0,S) rep(j,0,N) dp[i][j]=0;
    rep(i,1,S){
        rep(j,1,N) dp[i][j]=max(dp[i][j],dp[i-1][j]); //不選j位置。
        rep(j,1,N) if(Next[j]) dp[i][Next[j]]=max(dp[i][Next[j]],dp[i-1][j-1]+sum[Next[j]]-sum[j-1]); //選j
        rep(j,1,N) dp[i][j]=max(dp[i][j],dp[i][j-1]);
    }
    return dp[S][N]>=K;
}
int main()
{
    scanf("%d%d%d%d",&N,&M,&S,&K);
    rep(i,1,N) scanf("%d",&a[i]),b[i]=a[i];
    rep(i,1,M) scanf("%d%d",&s[i].L,&s[i].R);
    rep(i,1,M) rep(j,s[i].L,s[i].R) Next[j]=max(Next[j],s[i].R);
    sort(b+1,b+N+1); int L=1,R=N,Mid,ans=-1;
    while(L<=R){
        Mid=(L+R)>>1;
        if(check(b[Mid])) ans=b[Mid],R=Mid-1;
        else L=Mid+1;
    }
    printf("%d\n",ans);
    return 0;
}