P2519 [HAOI2011]problem a

思路

神仙思路,就差一步就能想出来了。。。
看到第i个人给出的条件,发现有(a_i)个大于,(b_i)个小于并不好处理
考虑把条件转化成第i个人对应的排名处理,设第i个人的排名为(a_i+1),则应当有(n-a_i-b_i)个和第i个人成绩相同的人
数形结合一下
把第i个人的条件看成是一个区间,区间开头是(a_i+1),结尾是(n-b_i),表示排名在这段区间中的人成绩相等
当两个人说的区间不重叠的时候,两个人都可以被判为真
这里要注意两个区间可以完全重合(两个人分数一致,说的都是一样的)(我就是没考虑这个)
有两种情况不可行(必然是谎言),一是左端点大于右端点,二是有超过区间长度的人都说了一样的话(这样只有区间长度个人说了真话)
于是给每个区间赋权值表示说这个区间的人数,所以要求的就是选出一些不相交区间使得权值最大(选出的人都说真话)
dp一下
转移方程是(dp[i]=max(dp[i-1],dp[k]+E[i].val)),k是最后一个右端点小于i的左端点的区间
k可以二分的找到,所以复杂度是(O(nlog n))

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
int n,f[100100],a[100100],b[100100],cnt;
struct Inter{
    int s,t,val;
}E[100100];
bool cmp(Inter a,Inter b){
    return a.t<b.t||(a.t==b.t&&a.s<b.s);
}
int find(int l,int r,int val){
    int midans=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(E[mid].t<val)
            midans=mid,l=mid+1;
        else
            r=mid-1;
    }
    return midans;
}
map<pair<int,int> , int> M;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d %d",&a[i],&b[i]);
        int len=n-a[i]-b[i];
        int lx=a[i]+1,rx=lx+len-1;
        if(rx>=lx)
            M[make_pair(lx,rx)]=min(rx-lx+1,M[make_pair(lx,rx)]+1);
    }
    for(auto it = M.begin();it!=M.end();it++){
        E[++cnt].s=(*it).first.first;
        E[cnt].t=(*it).first.second;
        E[cnt].val=(*it).second;
    }
    sort(E+1,E+cnt+1,cmp);
    f[1]=E[1].val;
    for(int i=2;i<=n;i++)
        f[i]=max(f[i-1],f[find(1,i-1,E[i].s)]+E[i].val);
    printf("%d
",n-f[n]);
    return 0;
}

原文地址:https://www.cnblogs.com/dreagonm/p/10552240.html