[区间dp][二分] Bzoj P2298 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]
  • 那么可以发现,有交集的两个区间里必定有一个是假的
  • 这样问题就转化为了给了n个区间,求最多不相交的区间数
  • 首先,可以先排除掉a+1>n-b的
  • 还要将相同区间的合并,而且不能超过可合并的个数(也就是r-l+1)
  • 最后就是dp了,设f[i]为前i个区间中不相交的最多区间数
  • 考虑转移:
  • ①f[i]=f[i-1],也就是当前区间不用了
  • ②可以在前面以右端点排过序的区间里,二分一个相邻最近的区间的f[x]加上e[i].d(也就是这个区间合并了多少个区间数)

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 using namespace std;
 6 const int inf=1e9+7;
 7 struct edge { int l,r,d; }e[100010];
 8 int k,f[100010],n,m;
 9 bool cmp(edge a,edge b) { return a.r==b.r?a.l<b.l:a.r<b.r; }
10 int find(int x)
11 {
12     int l=1,r=x-1,mx=0;
13     while (l<=r)
14     {
15         int mid=(l+r)/2;
16         if (e[mid].r<e[x].l) mx=mid,l=mid+1; else r=mid-1;
17     }
18     return f[mx];
19 }
20 int main()
21 {
22     scanf("%d",&n); m=n;
23     for (int i=1;i<=n;i++)
24     {
25         int x,y;
26         scanf("%d%d",&x,&y);
27         e[i].l=x+1; e[i].r=n-y; e[i].d=1;
28         if (e[i].r<e[i].l) e[i].r=inf;
29     }
30     sort(e+1,e+n+1,cmp);
31     k=1;
32     for (int i=2;i<=n;i++)
33         if (e[i].l==e[k].l&&e[i].r==e[k].r&&e[i].d+e[k].d<=e[k].r-e[k].l+1)    e[k].d+=e[i].d,e[i].r=inf;
34         else k=i;
35     sort(e+1,e+n+1,cmp);
36     for (int i=1;i<=n;i++) if (e[i].r==inf) n=i-1;
37     for (int i=1;i<=n;i++) f[i]=max(f[i-1],find(i)+e[i].d);
38     printf("%d",m-f[n]);
39     return 0;
40 }

 

posted @ 2018-08-07 20:00  BEYang_Z  阅读(183)  评论(0编辑  收藏  举报