[HAOI2011]problem a

时间:2023-03-08 23:24:05
[HAOI2011]problem a

题目大意:

网址:https://www.luogu.org/problemnew/show/2519
大意:
一次考试共有\(n\)个人参加,
第\(i\)个人说:“有\(a_i\)个人分数比我高,\(b_i\)个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
数据范围:$ 1≤n≤100000 0≤a_i、b_i≤n$

题目解法:

有\(a_i\)个人分数比我高,\(b_i\)个人分数比我低,
意味着这个人在成绩序列中处在\([a_i+1 , n - b_i]\)中。
并且\([a_i+1 , n - b_i]\)这个区间中的人成绩都是一样的。
显然可以\(DP\)了。
\(f[i]\)表示到了成绩序列的第\(i\)个位置,说真话的最大人数。
那么假设存在这样一个区间\([t,i]\),说自己属于此区间的有\(sum\)个人。
则转移:\[f[i] = max(f[i] , f[t-1] + sum);\]
用\(vector\)挂链,然后大力\(DP\)跑即可啦。(这题难度主要在模型的转化)

实现代码:

#include<bits/stdc++.h>
#define maxn 1000005
#define ll long long
#define gi(x) scanf("%lld",&x)
using namespace std;

vector<ll>gp[maxn]; ll nw,n,a,b,l,r,f[maxn],t[maxn];

int main(){
    gi(n);
    for(ll i = 1; i <= n; i ++){
        gi(a); gi(b);
        l = a+1; r = n-b;
        if(l<=r)gp[r].push_back(l);   //如果l>r那么肯定是假话。
    }
    for(ll i = 1; i <= n; i ++){
        f[i] = f[i-1];
        if(gp[i].empty())continue;
        int m = gp[i].size();
        for(ll j = 0; j < m; j ++)t[j+1] = gp[i][j];
        sort(t+1,t+m+1);
        for(ll j = 1,sum = 0; j <= m+1; j ++){
            if(t[j]==t[j-1]){sum++;continue;}
            if(sum)f[i] = max(f[i] , f[t[j-1]-1] + min(i-t[j-1]+1 , sum));
            sum = 1;
        }
    }
    cout<<n - f[n];  return 0;
}