D. Cut and Stick

给定一个长度为 n n n的数组,里面元素为 a 1 , a 2 , a 3 , … , a n − 1 , a n , ( 1 ≤ a i ≤ n ) a_1, a_2, a_3, \dots, a_{n- 1}, a_n, (1 \leq a_i \leq n) a1,a2,a3,,an1,an,(1ain),有 m m m次询问,每次给定 l , r l, r l,r

问如果把 [ l , r ] [l, r] [l,r]划分若干段子序列,假设其中一段长度为 l e n len len,里面出现最多次的元素次数 ≤ ⌈ l e n 2 ⌉ \leq \lceil \frac{len}{2} \rceil 2len,对所有段都满足,

不考虑划分成一段的情况,那么我们就是要尽可能地把最大地给划开,最优的策略是两个最大的跟一个其他的一起划分,

假设区间长度为 r − l + 1 = l e n r - l + 1 = len rl+1=len,最大的元素个数为 x x x,则还有 l e n − x len - x lenx个其他元素,

由于其他元素都是小于 ⌈ l e n 2 ⌉ \lceil \frac{len}{2} \rceil 2len的,所以,我们让两个最大元素与其他元素配对时,可以保证剩下的元素中最大个数不会超过 x x x的值,

假设我们要配对 k k k个,则当满足, x − 2 k + l e n − x − k + 1 ≥ 2 x − 4 k x - 2k + len - x - k + 1 \geq 2x - 4k x2k+lenxk+12x4k时只需要一个序列即可,解得 k = 2 x − l e n − 1 k = 2x - len - 1 k=2xlen1

所以最后答案为 m a x ( 2 x − l e n , 1 ) max(2x - len, 1) max(2xlen,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;
}