P2519 [HAOI2011]problem a

problem a

\(n\) 个人考试,第 \(i\) 个人说有 \(a_i\) 个人分数高于他,\(b_i\) 个低于他,求最少有多少个人在说谎。

很有意思的 DP 题,一开始以为是 2-sat 建模,然后发现不太行...。

转化一下他们说的话:“第 \(a_i+1\sim n-b_i\) 号人的分数和我一样”。

那么一定说谎的条件:

  1. \(a_i+1>n-b_i\)
  2. 有超过区间总长的人都认为 \(a_i+1\sim n-b_i\)

这些可以一开始直接排除,之后相同区间的人都可以合并到这个区间内,并作为这个区间的价值。

显然,相交的区间不能选择,例如有区间 \([a,b],[b,c]\),这说明 \(a=b>c,a>b=c\) 同时满足,这显然不可能。

容斥一下,问题变为,在序列内选择一些不相交的区间,使得总价值最大,这是比较经典的 DP 模型。

\(f(i)\) 表示前 \(i\) 区间能取得的最大价值,然后每次二分找到前面第一个不相交的区间即可。(似乎还可以 \(O(n)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define X first
#define Y second
#define MP make_pair
typedef pair<int, int> PII;
const int N = 1e5 + 10;

int n, t, f[N]; PII a[N];
struct Node{int l, r, v;} p[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool cmp(Node a, Node b){return a.r == b.r ? a.l < b.l : a.r < b.r;}

int Get(int limit, int l, int r){
	while(l < r){
		int mid = (l + r + 1) >> 1;
		if(p[mid].r < limit) l = mid;
		else r = mid - 1;
	}
	return f[l];
}

int main(){
	n = read();
	for(int i = 1; i <= n; i ++){
		int l = read(), r = read();
		a[i] = MP(l + 1, n - r);
	}
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; i ++){
		if(a[i].X > a[i].Y) continue;
		if(i == 1 || a[i] != a[i - 1])
			p[++ t] = (Node){a[i].X, a[i].Y, 1};
		else
			p[t].v ++, p[t].v = min(p[t].v, a[i].Y - a[i].X + 1);
	}
	sort(p + 1, p + t + 1, cmp);
	p[0] = (Node){0, 0, 0};
	for(int i = 1; i <= t; i ++){
		int val = Get(p[i].l, 0, i - 1);
		f[i] = max(f[i - 1], val + p[i].v);
	}
	printf("%d\n", n - f[t]);
	return 0;
}
posted @ 2021-07-20 19:43  LPF'sBlog  阅读(33)  评论(0编辑  收藏  举报