[区间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
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 }