problem a [HAOI2011]
题目传送门
题目描述:
一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
输入格式:
第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi
输出格式:
一个整数,表示最少有几个人说谎
样例输入:
3
2 0
0 2
2 2
样例输出:
1
数据范围:
30%的数据满足:1≤n≤1000
100%的数据满足:1≤n≤1000000≤ai、bi≤n
题解:
首先算出每个人的排名所能够位于的区间[l,r],即l=a+1,r=n-b。明显当l>r时是没有意义的,且该区间内的所有学生要同分。
再考虑如果两个人的区间无交集,那显然是互不影响的。如果两个人的区间有交集,比如学生a的排名区间为[1,2],b的排名区间为[2,3],假设两个人都说的是真话,那么根据上面的结论可得a的分数与b的分数一样。而比a分数高的人的数量为0,比b分数高的人的数量为1,这显然是不可能的。
如果两个人的排名区间一样,只要区间内位置足够,两个人就是不矛盾的。所以我们可以将排名相同的人的区间合并,权值相加。
最后问题就转化成了有m个区间,每个区间有一个权值,让你求最大互不相交区间权值和。可以用动态规划求解。
一个很容易想到的状态为f[i]表示将区间按右端点从小到大排序后前i个区间所能够得到的最大权值和,转移方程为:f[i] = max{f[j]+v[i]} ( j<i , r[j] < l[i] )。
但是n^2显然是超时的。
我们可以注意到f[i]是单调递增的,所以我们可以二分答案把复杂度降到n*logn。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define LL long long 6 #define RI register int 7 using namespace std; 8 const int INF = 0x7ffffff ; 9 const int N = 1000000 + 10 ; 10 11 inline int read() { 12 int k = 0 , f = 1 ; char c = getchar() ; 13 for( ; !isdigit(c) ; c = getchar()) 14 if(c == '-') f = -1 ; 15 for( ; isdigit(c) ; c = getchar()) 16 k = k*10 + c-'0' ; 17 return k*f ; 18 } 19 struct data { 20 int l, r, val ; 21 }p[N], lin[N] ; 22 int n ; int f[N], mm[N] ; 23 24 inline bool cmp1(data s,data t) { return s.l == t.l ? s.r < t.r : s.l < t.l ; } 25 inline bool cmp2(data s,data t) { return s.r == t.r ? s.l < t.l : s.r < t.r ; } 26 int main() { 27 // freopen("a.in","r",stdin) ; 28 // freopen("a.out","w",stdout) ; 29 n = read() ; int tot = 0 ; 30 for(int i=1;i<=n;i++) { 31 int x = read(), y = read() ; if(x+y >= n) continue ; 32 p[++tot].l = x+1, p[tot].r = n-y ; 33 } 34 sort(p+1,p+tot+1,cmp1) ; 35 int tt = 0 ; 36 for(int i=1;i<=tot;) { 37 lin[++tt].l = p[i].l, lin[tt].r = p[i].r, lin[tt].val = 1 ; int j ; 38 for(j=i+1;p[i].l == p[j].l && p[i].r == p[j].r;j++) lin[tt].val ++ ; 39 if(lin[tt].val > (lin[tt].r-lin[tt].l+1)) lin[tt].val = lin[tt].r-lin[tt].l+1 ; 40 i = j ; 41 } 42 sort(lin+1,lin+tt+1,cmp2) ; 43 for(int i=1;i<=tt;i++) { 44 int L = 1, R = i-1, k = 0 ; 45 while(L <= R) { 46 int mid = (L+R)>>1 ; 47 if(lin[mid].r < lin[i].l) k = max(k,mid), L = mid+1 ; 48 else R = mid-1 ; 49 } 50 f[i] = max(f[i-1],f[k]+lin[i].val) ; 51 } 52 printf("%d",n-f[tt]) ; 53 return 0 ; 54 }