首页 > 其他分享 >P8330 [ZJOI2022] 众数 题解

P8330 [ZJOI2022] 众数 题解

时间:2024-02-06 23:12:34浏览次数:27  
标签:P8330 颜色 int 题解 sqrt leq kMaxN 众数 ZJOI2022

Description

给定一个长度为 \(n\) 的序列 \(a_1,a_2,\dots,a_n\),问选一段区间,它的价值是里面的众数个数 \(+\) 外面的众数个数,求最大价值,以及所有满足这个最大价值的区间的外面的众数颜色。

\(\sum n\leq 5\times 10^5,n\leq 2\times 10^5\)。

Solution

考虑根号分治。

定义一个大的颜色指出现次数 \(>\sqrt n\) 的颜色,小的颜色指出现次数 \(\leq\sqrt n\) 的颜色。

首先考虑两个众数中至少一个是大颜色的贡献。

假设大颜色为 \(x\),另一个为 \(y\)。

第一种情况是形如 \(\texttt{xxxxyyyyyxxxx}\) 即 \(x\) 在两边,\(y\) 在中间的贡献。这时会发现一个区间 \([l,r]\) 的答案就是 \(x\) 的出现次数 \(+\) \([l,r]\) 中 \(y\) 的出现次数 \(-\) \([l,r]\) 中 \(x\) 的出现次数,这个东西很容易搞成区间和的形式,搞个最大子段和即可做到单次 \(O(n)\)。

容易发现最优情况一定满足选定区间的左右端点都是 \(y\),所以只要对于所有颜色为 \(y\) 的位置计算答案即可做到单次 \(O(cnt_y)\)。

对于 \(\texttt{yyyyxxxxxyyyy}\) 的情况是同理的,所以这一部分的总时间复杂度为 \(O(\sqrt n)\)。


然后考虑 小-小 的情况。

这里先枚举区间外的众数 \(x\),容易发现最优情况的区间 \([l,r]\) 一定满足:\(l=1\) 或 \(a_{l-1}=x\),和 \(r=n\) 或 \(a_{r+1}=x\)。

这时只要枚举这些 \([l,r]\) 再求出 \([l,r]\) 之间的众数即可得出答案。

但是这样要求 \(O(n\sqrt n)\) 次区间众数,显然过不了。

观察到这里只需要考虑 \([l,r]\) 中出现次数 \(\leq \sqrt n\) 的数的众数,所以 \([l,r]\) 的答案也 \(\leq \sqrt n\)。

于是可以依次枚举 \(r\),维护 \(s_l\) 表示 \([l,r]\) 的众数。然后对于所有 \(r\) 前且颜色和 \(r\) 相等的位置 \(i\),需要对于所有 \(j\leq i\) 的 \(s_j\) 对 \([i,r]\) 中 \(a_i\) 的个数取 \(\max\)。

注意到这里 \(s\) 一定是单调不增的,所以只需从后往前扫,如果能更新就更新,否则就退出。

然后右端点为 \(r+1\) 的答案即为 \(\max{c_i+cnt_{[1,i-1],a_{r+1}}+cnt_{[r+1,n],a_{r+1}}}\),这个暴力枚举颜色与 \(r+1\) 相同的 \(i\) 即可。

容易发现一次更新至少会使 \(s\) 的总和 \(+1\),而最终 \(s_i\leq \sqrt n\),所以时间复杂度就是 \(O(n\sqrt n)\)。

总的时间复杂度:\(O(n\sqrt n)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5;

int n, m, b;
int a[kMaxN], unq[kMaxN], pos[kMaxN], res[kMaxN], sum[kMaxN];
std::vector<int> v[kMaxN];

void discrete() {
  b = sqrt(n);
  for (int i = 1; i <= n; ++i)
    unq[i] = a[i];
  std::sort(unq + 1, unq + 1 + n);
  m = std::unique(unq + 1, unq + 1 + n) - (unq + 1);
  for (int i = 1; i <= n; ++i)
    v[i].clear(), res[i] = 0;
  for (int i = 1; i <= n; ++i) {
    a[i] = std::lower_bound(unq + 1, unq + 1 + m, a[i]) - unq;
    pos[i] = v[a[i]].size();
    v[a[i]].emplace_back(i);
  }
}

int big1(int x, int y) { // xxxxxyyyyyyyyyxxxxx
  static int pre1[kMaxN], pre2[kMaxN];
  int mi = 1e9, ret = 0;
  for (int i = 0; i < (int)v[y].size(); ++i) {
    pre1[i] = sum[v[y][i]] + i + 1;
    pre2[i] = sum[v[y][i] - 1] + i;
    mi = std::min(mi, pre2[i]);
    ret = std::max(ret, pre1[i] - mi);
  }
  return ret + v[x].size();
}

int big2(int x, int y) { // yyyyyxxxxxxyyyyyy
  static int pre[kMaxN], suf[kMaxN], mx[kMaxN];
  for (int i = 0; i < (int)v[y].size(); ++i) {
    pre[i] = sum[v[y][i]] + i + 1;
    suf[i] = sum[n] - sum[v[y][i] - 1] + v[y].size() - i;
    mx[i] = (i == 0 ? pre[i] : std::max(mx[i - 1], pre[i]));
  }
  int now = -1e9, ret = mx[(int)v[y].size() - 1];
  for (int i = (int)v[y].size() - 1; i; --i) {
    now = std::max(now, suf[i]);
    ret = std::max(ret, now + pre[i - 1]);
  }
  ret = std::max(ret, now);
  return ret + v[x].size();
}

void solve_big() {
  for (int x = 1; x <= m; ++x) {
    if (v[x].size() <= b) continue;
    for (int i = 1; i <= n; ++i)
      sum[i] = sum[i - 1] - (a[i] == x);
    for (int y = 1; y <= m; ++y) {
      res[x] = std::max(res[x], big1(x, y));
      res[y] = std::max(res[y], big2(x, y));
    }
  }
}

void solve_small() {
  static int c[kMaxN], cnt[kMaxN];
  // c[i] : [i, r] 的众数
  std::fill_n(c + 1, n, 0);
  std::fill_n(cnt + 1, n, 0);
  for (int i = 1; i <= n; ++i) {
    if (v[a[i]].size() > b) continue;
    res[a[i]] = std::max(res[a[i]], c[1] + (int)v[a[i]].size() - pos[i]);
    for (int j = pos[i] - 1; ~j; --j) {
      res[a[i]] = std::max(res[a[i]], c[v[a[i]][j] + 1] + (int)v[a[i]].size() - pos[i] + j + 1);
    }
    for (int j = pos[i]; ~j; --j) {
      for (int k = v[a[i]][j]; k && c[k] <= pos[i] - j + 1; --k)
        c[k] = pos[i] - j + 1;
    }
  }
  int mx = 0;
  for (int i = n; i; --i) {
    res[a[i]] = std::max(res[a[i]], mx + pos[i] + 1);
    mx = std::max(mx, ++cnt[a[i]]);
  }
}

void dickdreamer() {
  std::cin >> n;
  for (int i = 1; i <= n; ++i)
    std::cin >> a[i];
  discrete(), solve_big(), solve_small();
  int mx = 0;
  for (int i = 1; i <= m; ++i) mx = std::max(mx, res[i]);
  std::cout << mx << '\n';
  for (int i = 1; i <= m; ++i)
    if (res[i] == mx)
      std::cout << unq[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:P8330,颜色,int,题解,sqrt,leq,kMaxN,众数,ZJOI2022
From: https://www.cnblogs.com/Scarab/p/18010441

相关文章

  • 2024年2月6号题解
    P2872[USACO07DEC]BuildingRoadsS洛谷题目链接解题思路这个是一个最小生成树,把在平面直角坐标系中的点定义为顶点,把两个点的距离定义为边但这个题目并没有给出图中的边,也就是两个点的距离,因此我们要把这个图的边给补上,这个平面直角坐标系的点的每条边都是图上的边。但因......
  • 题解 AT_exawizards2019_e【Black or White】
    设\(P_b(k),P_w(k)\)表示已经吃了\(k\)块巧克力,把所有黑/白巧克力都吃光了的概率。枚举最后一块黑/白巧克力被吃的时间,容易得到:\[\begin{aligned}P_b(k)&=\sum_{i=1}^k\frac{\binom{i-1}{b-1}}{2^i}\\P_w(k)&=\sum_{i=1}^k\frac{\binom{i-1}{w-1}}{2^i}\\\end{aligned}\]......
  • 【题解】P5907 数列求和加强版 / P4948 数列求和
    本题解是对warzone的题解的补充。题意:求\[\sum_{i=1}^ni^ka^i\]普通版:\(k\leq2\times10^3\)。加强版:\(k\leq10^7\)。首先先考虑普通版。\[\begin{aligned}\sum_{i=1}^ni^ka^i&=\sum_{i=1}^na^i\sum_{j=0}^k{k\bracej}i^{\underline{j}}\\&=\sum_{j=0......
  • 蚯蚓排队题解
    蚯蚓排队题目描述蚯蚓幼儿园有\(n\)只蚯蚓。幼儿园园长神刀手为了管理方便,时常让这些蚯蚓们列队表演。所有蚯蚓用从\(1\)到\(n\)的连续正整数编号。每只蚯蚓的长度可以用一个正整数表示,根据入园要求,所有蚯蚓的长度都不超过\(6\)。神刀手希望这些蚯蚓排成若干个队伍,初始时,每只蚯......
  • Codeforces Round 921 (Div. 1) 题解
    HelloAd-HocForces!A字符集为前\(k\)个小写字母,给定长度为\(m\)的字符串,求所有的长度为\(n\)的字符串是否是这个字符串的子串。(此处字串不连续)如果不是需要给出反例。\(1\len,k\le26\),\(1\lem\le1000\)。\(\sumn,\summ\le10^6\)sol.D1A就是神秘贪心,汗流浃背......
  • U405333 帕鲁世界迷路的一天 题解
    题目链接:帕鲁世界迷路的一天前置弱化版:P3604美好的每一天题解一个非常简单的普通莫队解很容易写出来,具体的看我前置弱化版题解,然而这个复杂度高达\(O(26n\sqrt{q})\),显然无法通过强化版。一种看上去很正确的“假解”我们思考如何去掉这个\(26\),我们猜想:能够组成\(pre[c......
  • [ARC171] A~D 题解
    [ARC171]A~D题解A.NoAttacking最优策略是车隔行放,分讨一下就可以了。if(n<a)cout<<"No\n";else{if(a*2<n)b-=(a+1)*(n-a);else{b-=(n-a)*(n-a);if(b<=0)cout<<"Yes\n";......
  • P3219 [HNOI2012] 三角形覆盖问题&P1222 三角形 题解
    严格单$\log$做法,附实现细节和代码。考虑从左往右扫描线,发现每次操作是线段上端点$-1$,插入线段,删除退化成点的线段。如果某时刻一条线段被另一条完全覆盖,那么这条线段显然不会产生贡献,可以删去。最后得到的线段一定是左端点单调递增时,右端点也单调递增,可以用set维护。当两......
  • P10145 [WC/CTS2024] 线段树 题解
    Link纪念一下场切题。题意:给定一棵(分点不一定为中点)的线段树,给定若干个询问区间,问有多少个线段树上结点的集合,知道了这些结点对应的区间和就可以知道任何一个询问区间的和。从询问区间开始考虑。会发现可以把所有\(a_i\)分成若干个集合,只要知道每个集合的和就可以知道所有询......
  • 浅谈甲类生产厂房应急照明和疏散指示系统的设计问题解析
    摘要:文章结合电气设计项目实践经验,分析了甲类生产厂房消防应急照明和疏散指示系统设计中照明灯和标志灯的设置、疏散走道和疏散通道的规划、集中控制型系统类型的选择、电压等级的选择中存在的问题,总结了相关经验,可以为同类工程提供参考。关键词:甲类生产厂房;消防应急照明和疏散指示......