【BZOJ2298】[HAOI2011]problem a DP
【BZOJ2298】[HAOI2011]problem a
Description
一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
Input
第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi
Output
一个整数,表示最少有几个人说谎
Sample Input
3
2 0
0 2
2 2
2 0
0 2
2 2
Sample Output
1
HINT
100%的数据满足: 1≤n≤100000 0≤ai、bi≤n
题解:先得出每个人可能的名次分布区间[li,ri],然后考虑那些说真话的人的区间是什么样。发现最终说真话的人的区间一定呈如下形式:若干个[l1,r1],若干个[l2,r2]。。。若干个[li,ri],并且l1<r1<l2<r2...<li<ri。所以这题本质上就是问你最多能选多少个互不包含的区间。所以将说话相同的人合并到一起,然后用前缀最大值维护s[i],表示右端点为i时,以前最多有多少人说真话即可。
然而我这个沙茶用了树状数组。。。。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn=100010; struct node { int a,b; }p[maxn]; int n; int s[maxn]; bool cmp(const node &a,const node &b) { return (a.b==b.b)?(a.a<b.a):(a.b<b.b); } inline void updata(int x,int val) { for(int i=x+1;i<=n+1;i+=i&-i) s[i]=max(s[i],val); } inline int query(int x) { int i,ret=0; for(i=x+1;i;i-=i&-i) ret=max(ret,s[i]); return ret; } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(); int i,j,a,b; for(i=1;i<=n;i++) a=rd(),b=rd(),p[i].a=b,p[i].b=n-a; sort(p+1,p+n+1,cmp); for(i=1;i<=n;i=j+1) { for(j=i;j<n&&p[j+1].a==p[j].a&&p[j+1].b==p[j].b;j++); if(p[i].a>=p[i].b) continue; updata(p[i].b,query(p[i].a)+min(j-i+1,p[i].b-p[i].a)); } printf("%d",n-query(n)); return 0; }
| 欢迎来原网站坐坐! >原文链接<