【题解】P2519 [HAOI2011]problem a

版权声明:Powered By Fighter https://blog.csdn.net/qq_30115697/article/details/89372587

原题传送门

Solution

这题难在题意的转化。(同时说一句:STL大法好啊!!)

题目里给定了对于第 i i 个人,有 a i a_i 个人分数比他高, b i b_i 个人分数比他低,那么我们可以知道他的排名为 a i + 1 a_i+1 ,并且有 n a i b i n-a_i-b_i 个人(包括自己)与他分数相等。那么我们就可以知道这段分数相等的区间为 [ a i + 1 , n b i ] [a_i+1, n-b_i]

显然如果 n b i > a i + 1 n-b_i > a_i+1 ,那么这个人说的一定是假话。同时如果有多个人分数相同,即在同一区间中,那么这些人的数量显然不能超过区间长度,如果超过,那么多余的这些人一定在说假话。

接着考虑怎么实现。首先肯定要记录同在一个区间内的人的数量,于是我们搬出STL——map。我们把区间插入到map中,并且记录当前区间的人数,对其进行累加操作。

接着把题目中“最小说假话人数”转换为“最大说真话人数”。我们定义 v x , y v_{x,y} 表示区间 [ x , y ] [x,y] 中的人数,那么我们要求的就是从总区间 [ 1 , n ] [1,n] 中选取几段不重叠的区间,使得区间的 v v 之和最大。于是考虑 d p dp

f i f_i 表示从 [ 1 , i ] [1,i] 中选取不重叠的区间的 v v 之和的最大值,那么首先显然要继承 f i 1 f_{i-1} 的值。

接着考虑选取以 i i 为右端点的区间,设此区间左端点为 x x ,那么显然:
f i = f x 1 + m i n ( v x , i , i x + 1 ) f_i = f_{x-1}+min(v_{x,i}, i-x+1)
后面取 m i n min 正是为了保证区间内人数不超过区间长度。

然后为了方便的找到以 i i 为右端点的区间,我们在读入的时候进行预处理,开一个vector数组 l l (STL nb!!) l [ i ] l[i] 存放所有以 i i 为右端点的区间的左端点,这样在 d p dp 的时候就可以快速计算。

复杂度:由于 m a p map ,所以有一只 l o g log ,总复杂度 O ( n l o g n ) O(n·logn)

Code

别忘了最后答案是 n f n n-f_n

#include <bits/stdc++.h>
#define MAX 100005
using namespace std;

vector<int> l[MAX];
map<pair<int, int>, int> mp;
int n, f[MAX];

int main()
{
    cin >> n;
    int x, y;
    for(int i = 1; i <= n; i++){
        scanf("%d%d", &x, &y);
        x++, y = n-y;
        if(x > y) continue;
        int t = ++mp[make_pair(x, y)];
        if(t == 1){
            l[y].push_back(x);
        }
    }
    
    for(int i = 1; i <= n; i++){
        f[i] = f[i-1];
        for(int j = 0; j < l[i].size(); j++){
            x = l[i][j];
            f[i] = max(f[i], f[x-1]+min(i-x+1, mp[make_pair(x, i)]));
        }
    }
    cout << n-f[n] << endl;
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/89372587