bzoj 2298: [HAOI2011]problem a
Description
一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
Input
第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi
Output
一个整数,表示最少有几个人说谎
Sample Input
3
2 0
0 2
2 2
题解:
很好想的题啊,题目也短短的,我喜欢..
考虑题目给的a,b的含义
如果a+b==n-1那么这个人的排名就固定了
反之 那么这个人就可以成为[a+1,n-b]中的任意一个排名....
还有一个关键就是 如果承认了一个人 就等于承认了[a+1,n-b]这个区间的人必须分数相等 且其他人不得与[a+1,n-b]的人相等
那么就可以转换成线段覆盖问题,f[i]=f[j]+1 (如果i,j不相交)
但是如果区间重合了 我们就可以累加,所以转化为f[i]=f[j]+min(val,r[i]-l[i]+1) (val为和i重合的区间个数 但不得超过该区间大小)
最后树状数组存 f[j]+val 优化下即可
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 using namespace std; 8 const int N=100005; 9 int n; 10 struct node{ 11 int l,r,val; 12 bool operator <(const node &pp){ 13 if(pp.r!=r)return r<pp.r; 14 return l<pp.l; 15 } 16 }a[N]; 17 int Tree[N]; 18 void updata(int sta,int x){ 19 for(int i=sta;i<=n;i+=(i&(-i))) 20 if(Tree[i]<x)Tree[i]=x; 21 } 22 int query(int sta){ 23 int ret=0; 24 for(int i=sta;i>=1;i-=(i&(-i))) 25 if(Tree[i]>ret)ret=Tree[i]; 26 return ret; 27 } 28 void work() 29 { 30 int x,y,m=0; 31 scanf("%d",&n); 32 for(int i=1;i<=n;i++){ 33 scanf("%d%d",&x,&y); 34 x++;y=n-y; 35 if(x>y)continue; 36 a[++m].l=x;a[m].r=y;a[m].val=0; 37 } 38 sort(a+1,a+m+1); 39 int tot=0,t,ret=0; 40 for(int i=1;i<=m;i++){ 41 if(a[tot].l==a[i].l && a[tot].r==a[i].r)a[tot].val++; 42 else a[++tot]=a[i],a[tot].val=1; 43 } 44 for(int i=1;i<=tot;i++) 45 if(a[i].r-a[i].l+1<a[i].val)a[i].val=a[i].r-a[i].l+1; 46 updata(a[1].r,a[1].val); 47 for(int i=2;i<=tot;i++){ 48 t=query(a[i].l-1)+a[i].val; 49 if(t>ret)ret=t; 50 updata(a[i].r,t); 51 } 52 printf("%d\n",n-ret); 53 } 54 55 int main() 56 { 57 work(); 58 return 0; 59 }