按中点将线段排序,枚举选择前几个人去听第一个老师的课,剩下的自然去到第二个老师。
枚举两个老师选择的区间,每次统计当前选择的两部分人与此区间的重合区间和,维护差分前缀和。
差分前缀和还要统计区间和,由于众所周知本人很懒,不想用一个数组倒腾,于是开了两个树状数组。时间复杂度$O(n^2logn)$,用一个数组乱搞也只是伪$n^2$,妹有意思。
#include<bits/stdc++.h> using namespace std; int n, m, k; struct Node { int l, r; } stu[2005]; bool cmp(Node a, Node b) { return (a.l + a.r) < (b.l + b.r); } int f[2005], las[2005], pre[2005], ini[2005]; int tr1[2005]; int lowbit(int x) { return x & (-x); } void add1(int pos, int d) { for(int i = pos; i <= n; i += lowbit(i)) tr1[i] += d; } int query1(int pos) { int ans = 0; for(int i = pos; i; i -= lowbit(i)) ans += tr1[i]; return ans; } int tr2[2005]; void add2(int pos, int d) { for(int i = pos; i <= n; i += lowbit(i)) tr2[i] += d; } int query2(int pos) { int ans = 0; for(int i = pos; i; i -= lowbit(i)) ans += tr2[i]; return ans; } int main() { scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= m; i ++) { int a, b; scanf("%d%d", &a, &b); stu[i].l = a, stu[i].r = b; f[a] ++, f[b + 1] --; } sort(stu + 1, stu + m + 1, cmp); int ans = 0; for(int i = 1; i <= m; i ++) { ini[stu[i].l] ++, ini[stu[i].r + 1] --; int maxn = 0; memset(pre, 0, sizeof(pre)); memset(tr1, 0, sizeof(tr1)); for(int j = 1; j <= n; j ++) { pre[j] = pre[j - 1] + ini[j]; add1(j, pre[j]); if(j >= k) maxn = max(maxn, query1(j) - query1(j - k)); } f[stu[i].l] --, f[stu[i].r + 1] ++; int maxm = 0; memset(las, 0, sizeof(las)); memset(tr2, 0, sizeof(tr2)); for(int j = 1; j <= n; j ++) { las[j] = las[j - 1] + f[j]; add2(j, las[j]); if(j >= k) maxm = max(maxm, query2(j) - query2(j - k)); } ans = max(ans, maxm + maxn); } printf("%d", ans); return 0; }
这道题是夜宵前大佬们累了俺想出来的耶!