D. Cut and Stick
给定一个长度为nnn的数组,里面元素为a1,a2,a3,…,an−1,an,(1≤ai≤n)a_1, a_2, a_3, \dots, a_{n- 1}, a_n, (1 \leq a_i \leq n)a1,a2,a3,…,an−1,an,(1≤ai≤n),有mmm次询问,每次给定l,rl, rl,r,
问如果把[l,r][l, r][l,r]划分若干段子序列,假设其中一段长度为lenlenlen,里面出现最多次的元素次数≤⌈len2⌉\leq \lceil \frac{len}{2} \rceil≤⌈2len⌉,对所有段都满足,
不考虑划分成一段的情况,那么我们就是要尽可能地把最大地给划开,最优的策略是两个最大的跟一个其他的一起划分,
假设区间长度为r−l+1=lenr - l + 1 = lenr−l+1=len,最大的元素个数为xxx,则还有len−xlen - xlen−x个其他元素,
由于其他元素都是小于⌈len2⌉\lceil \frac{len}{2} \rceil⌈2len⌉的,所以,我们让两个最大元素与其他元素配对时,可以保证剩下的元素中最大个数不会超过xxx的值,
假设我们要配对kkk个,则当满足,x−2k+len−x−k+1≥2x−4kx - 2k + len - x - k + 1 \geq 2x - 4kx−2k+len−x−k+1≥2x−4k时只需要一个序列即可,解得k=2x−len−1k = 2x - len - 1k=2x−len−1,
所以最后答案为max(2x−len,1)max(2x - len, 1)max(2x−len,1),所以我们只要统计区间数量最多的值即可,可用莫队解决。
#include <bits/stdc++.h>using namespace std;const int N = 1e6 + 10;int a[N], ans[N], sum[N], num[N], n, m, block, maxn;struct Res {int l, r, id;
}query[N];bool cmp(Res x, Res y) {return ((x.l / block) != (y.l / block)) ? x.l < y.l : ((x.l / block) & 1) ? x.r < y.r : x.r > y.r;
}void add(int x) {num[a[x]]++;sum[num[a[x]]]++;while (sum[maxn + 1]) {maxn++;}
}void del(int x) {sum[num[a[x]]]--;num[a[x]]--;while (maxn && !sum[maxn]) {maxn--;}
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);scanf("%d %d", &n, &m);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);}block = sqrt(n);for (int i = 1, l, r; i <= m; i++) {scanf("%d %d", &l, &r);query[i] = {l, r, i};}sort(query + 1, query + 1 + m, cmp);int l = 1, r = 0;for (int i = 1; i <= m; i++) {while (r < query[i].r) {add(++r);}while (l < query[i].l) {del(l++);}while (r > query[i].r) {del(r--);}while (l > query[i].l) {add(--l);}ans[query[i].id] = max(1, 2 * maxn - (query[i].r - query[i].l + 1));}for (int i = 1; i <= m; i++) {printf("%d\n", ans[i]);}return 0;
}