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;
}
标签:ch,P2519,color,bi,0066ff,ai,HAOI2011,区间,problem 来源: https://www.cnblogs.com/olinr/p/10343945.html