[二分][DP]BZOJ 2298 HAOI problem a

Description

一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)

Input

第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi

Output

一个整数,表示最少有几个人说谎

Sample Input

3
2 0
0 2
2 2

Sample Output

1

HINT

100%的数据满足: 1≤n≤100000   0≤ai、bi≤n

分析

这题容易想到如果一个人说有a人高过自己,b人低过自己,那么显然从a+1~n-b之间的人分数相等,这就是一段线段。

那么容易想到说假话的人一定线段相交或包含(重合不算)

这个比较难求,我们可以转化一下:求说真话的,显然只有互不相交和重合

所以我们把同样的线段合并,加权(注意权值不能超过线段长度)

然后做DP,设f[i]为做到第i条线段的时候的最大总权。(要给线段排个序)二分求1~i-1的满足r[j]<l[i]的最大f[j]即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N=100001;
struct Segment {
    int l,r;
}a[N];
int n,cn;
int s[N],f[N];

bool Cmp(Segment a,Segment b) {
    return a.r<b.r||a.r==b.r&&a.l<b.l;
}

int Bin_Search(int l,int r,int num) {
    int mid=l+r>>1;
    while (l<r) {
        mid=l+r>>1;
        if (a[mid].r<num) l=mid+1;
        else r=mid-1;
    }
    return r;
}

int main() {
    int cnt=0;
    scanf("%d",&n);cn=n;
    for (int i=0;i<n;i++) {
        scanf("%d%d",&a[cnt].l,&a[cnt].r);
        a[cnt].l++;a[cnt].r=n-a[cnt].r;
        if (a[cnt].l<=a[cnt].r) cnt++;
    }
    sort(a,a+cnt,Cmp);
    n=-1;
    for (int i=0;i<cnt;i++)
    if (!i||a[i].l!=a[i-1].l||a[i].r!=a[i-1].r) a[++n]=a[i],s[n]=1;
    else s[n]=min(s[n]+1,a[n].r-a[n].l+1);
    f[0]=s[0];
    for (int i=1;i<=n;i++)
    f[i]=max(f[i-1],f[Bin_Search(0,i-1,a[i].l)]+s[i]);
    printf("%d",cn-f[n]);
}
View Code

 

posted @ 2018-08-07 20:24  Vagari  阅读(125)  评论(0编辑  收藏  举报