1. 程式人生 > >【BZOJ2298】[HAOI2011]problem a DP

【BZOJ2298】[HAOI2011]problem a DP

最大 一起 class min rip ret esc output 並且

【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;
}

【BZOJ2298】[HAOI2011]problem a DP