P2519 [HAOI2011]problem a

\(\color{#0066ff}{ 题目描述 }\)

一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)

$\color{#0066ff}{ 输入格式 } $

第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi

\(\color{#0066ff}{输出格式}\)

一个整数,表示最少有几个人说谎

\(\color{#0066ff}{输入样例}\)

3
2 0
0 2
2 2

\(\color{#0066ff}{输出样例}\)

1

\(\color{#0066ff}{数据范围与提示}\)

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

\(\color{#0066ff}{ 题解 }\)

把问题转换一下

考虑最后的排名,假设我们按成绩从大到小排

那么可以发现,每个人的条件变成了\([a_i+1,n-b_i]\)的人成绩一样

然后就转化为了区间问题。。。

首先,那些区间r<l的。。显然是假话

然后可以发现,任何两个区间一旦非重合相交,就有人说假话,不合法

于是我们把l和r都相同的人合并,记区间权值为等于这个区间的人的个数

于是问题变为,选出尽量多不相交,权值尽量大

序列DP。

\(f[i]\)表示进行到i位置的最大值,把区间排个序,维护一个单调指针转移即可

注意答案一部分是n-f,一部分是不合法的人!

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const int maxn = 1e5 + 100;
struct node {
	int l, r, num;
}a[maxn]; 
int n, f[maxn];
bool cmp1(const node &a, const node &b) {
	return (a.l < b.l || (a.l == b.l && a.r < b.r));
}
bool cmp2(const node &a, const node &b) {
	return a.r < b.r;
}
int main() {
	int _  = in();
	for(int i = 1; i <= _; i++) {
		a[i].l = in() + 1, a[i].r = _ - in();
		if(a[i].l <= a[i].r) a[++n] = a[i];
		a[n].num = 1;
	}
	std::sort(a + 1, a + n + 1, cmp1);
	int tot = 1;
	for(int i = 2; i <= n; i++) {
		if(a[i].l == a[i - 1].l && a[i].r == a[i - 1].r) a[tot].num++;
		else a[++tot] = a[i];
	}
	int now = 1;
	std::sort(a + 1, a + tot + 1, cmp2);
	for(int i = 1; i <= tot; i++) a[i].num = std::min(a[i].num, a[i].r - a[i].l + 1);
	for(int i = 1; i <= _; i++) {
		f[i] = f[i - 1];
		while(now <= tot && a[now].r == i) {
			f[i] = std::max(f[i], f[a[now].l - 1] + a[now].num);
			now++;
		}
	} 
	printf("%d\n", _ - f[_]);
	return 0;
}
posted @ 2019-01-31 20:32  olinr  阅读(172)  评论(0编辑  收藏  举报