[二分][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
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]); }
在日渐沉没的世界里,我发现了你。