[HAOI 2011] Problem A

[题目链接]

         https://www.lydsy.com/JudgeOnline/problem.php?id=2298

[算法]

         考虑用总人数 - 最多人说真话

         显然 , 对于每个人 , 如果他说的是真话 , 那么他的排名必然在[ai + 1 , n - bi]中 , 否则不合法

         统计出每个合法区间相同的个数

         那么问题转化为了 :

         现在有一些线段 , 每条线段[li , ri]有一个权值wi , 从中选取若干条使得权值和最大

         考虑dp 

         将区间按右端点排序 , 用fi表示前i个区间的最大权值和 , 通过二分求出最大的pos使得rpos < li

         有转移方程fi = max{fi-1 , fpos + w}

         答案即为n - fn

         时间复杂度 : O(NlogN)

[代码]

         

#include<bits/stdc++.h>
using namespace std;
#define N 100010
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

struct segment
{
        int l , r;
} s[N];
struct info
{
        int l , r;
        int value;
} e[N];

int n , m , tot;
int a[N] , b[N] , dp[N];

template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); }
template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); }
template <typename T> inline void read(T &x)
{
    T f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}
inline bool cmpa(segment a , segment b)
{
        if (a.l != b.l) return a.l < b.l;
        else return a.r < b.r;
}
inline bool cmpb(info a , info b)
{
        return a.r < b.r;        
}

int main()
{
        
        read(n);
        for (int i = 1; i <= n; ++i)
        {
                read(a[i]);
                read(b[i]);
                if (a[i] + 1 <= n - b[i])    
                        s[++tot] = (segment){a[i] + 1 , n - b[i]};    
        }
        sort(s + 1 , s + tot + 1 , cmpa);
        for (int i = 1; i <= tot; ++i)
        {
                if (s[i].l == s[i - 1].l && s[i].r == s[i - 1].r)    
                {
                        if (e[m].value != s[i].r - s[i].l + 1) 
                                ++e[m].value;
                        continue;
                } else
                        e[++m] = (info){s[i].l , s[i].r , 1}; 
        }
        sort(e + 1 , e + m + 1 , cmpb);
        for (int i = 1; i <= m; ++i)
        {
                int l = 1 , r = i , k = 0;
                while (l <= r)
                {
                        int mid = (l + r) >> 1;
                        if (e[mid].r < e[i].l)
                        {
                                k = mid;
                                l = mid + 1;
                        } else r = mid - 1;
                }        
                dp[i] = max(dp[i - 1] , dp[k] + e[i].value);
        }
        printf("%d\n" , n - dp[m]);
        
        return 0;
    
}

 

posted @ 2019-03-29 20:57  evenbao  阅读(145)  评论(0编辑  收藏  举报