[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; }