【BZOJ2298】[HAOI2011]problem a DP

【BZOJ2298】[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

Sample Output

1

HINT

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

题解:先得出每个人可能的名次分布区间[li,ri],然后考虑那些说真话的人的区间是什么样。发现最终说真话的人的区间一定呈如下形式:若干个[l1,r1],若干个[l2,r2]。。。若干个[li,ri],并且l1<r1<l2<r2...<li<ri。所以这题本质上就是问你最多能选多少个互不包含的区间。所以将说话相同的人合并到一起,然后用前缀最大值维护s[i],表示右端点为i时,以前最多有多少人说真话即可。

然而我这个沙茶用了树状数组。。。。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100010;
struct node
{
	int a,b;
}p[maxn];
int n;
int s[maxn];
bool cmp(const node &a,const node &b)
{
	return (a.b==b.b)?(a.a<b.a):(a.b<b.b);
}
inline void updata(int x,int val)
{
	for(int i=x+1;i<=n+1;i+=i&-i)	s[i]=max(s[i],val);
}
inline int query(int x)
{
	int i,ret=0;
	for(i=x+1;i;i-=i&-i)	ret=max(ret,s[i]);
	return ret;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i,j,a,b;
	for(i=1;i<=n;i++)	a=rd(),b=rd(),p[i].a=b,p[i].b=n-a;
	sort(p+1,p+n+1,cmp);
	for(i=1;i<=n;i=j+1)
	{
		for(j=i;j<n&&p[j+1].a==p[j].a&&p[j+1].b==p[j].b;j++);
		if(p[i].a>=p[i].b)	continue;
		updata(p[i].b,query(p[i].a)+min(j-i+1,p[i].b-p[i].a));
	}
	printf("%d",n-query(n));
	return 0;
}
posted @ 2017-09-24 10:27  CQzhangyu  阅读(251)  评论(0编辑  收藏  举报