CF786C Till I Collapse 做题笔记

发布于

题意

对于每个 $k \in \left[1, n\right]$,输出最小的 $m$,满足存在一种将给定的 $n$ 个数划分成 $m$ 段的方案,每段中不同数字的个数不超过 $k$ 个。

题解

做法 1

我一开始想到的就是这个。首先观察到我们一段区间里至少会有 $k$ 个数,因此可以暴力枚举 $k$,然后快速跳。这样是一个调和数。

我们需要对于一个点 $l$,快速找到一个 $r$,使得 $\left[l, r\right]$ 中的不同数个数是 $k + 1$ 个,而 $\left[l, r\right)$ 中的不同数个数是 $k$ 个。二分之后是区间数颜色问题。

区间数不同数个数,我们对于每个 $i$,预处理出 $p_i$,跟 $i$ 颜色相同的在 $i$ 之前的最后一个位置。这样,对于一个查询区间 $\left[l, r\right]$ 来讲,如果 $l \leq i \leq r$ 且 $p_i < l$,那么将颜色加一。

我们可以用主席树或者线段树套 vector 查询。

线段树上二分可以做到 $O\left(n \log^3 n\right)$。主席树上二分可以做到 $O\left(n \log^2 n\right)$。

由于写挂了调不出来,没有代码。

做法 2

根号分治,对于 $k \leq B$,暴力 $O\left(n\right)$ 计算。

对于 $k > B$,不同的答案只有最多 $\frac nB$ 种,答案相同的 $k$ 连续,于是可以对于每个答案二分 $k$ 的区间。

复杂度是 $O\left(Bn + \frac nB n \log n\right)$,取 $B = \sqrt{n \log n}$ 达到最优复杂度 $O\left(n\sqrt{n \log n}\right)$。

代码

/* C++20 is required!
 * By Koicy (https://koicy.ly)
 * Email n@rbtr.ee
 * 我还期待着名为奇迹的魔法,哪怕会凋零会消散只会是一场浮华,我笑纳!

         __    __                             __         _
   _____/ /_  / /_________  ___              / /______  (_)______  __
  / ___/ __ \/ __/ ___/ _ \/ _ \   ______   / //_/ __ \/ / ___/ / / /
 / /  / /_/ / /_/ /  /  __/  __/  /_____/  / ,< / /_/ / / /__/ /_/ /
/_/  /_.___/\__/_/   \___/\___/           /_/|_|\____/_/\___/\__, /
                               SIGN                         /___*/
#include <bits/stdc++.h>
#define __NO_MAIN__ false
constexpr bool _MTS = false, SPC_MTS = false;
#define FULL(arg) arg.begin(), arg.end()

// :/

using namespace std;
using tp = long long int;
using vetp = basic_string<tp>;
[[maybe_unused]] constexpr tp ZERO = 0, ONE = 1, INF = -1ull >> 2;
int STRUGGLING(unsigned long long int);
void MIST();

#if !__NO_MAIN__
int main(int argc,char* argv[]){unsigned long long int t=0,_t=1;MIST();if(_MTS&&
!SPC_MTS)cin>>_t;while(t<_t||SPC_MTS){if(STRUGGLING(++t)!=0)return 0;}return 0;}
#endif
template <typename _Ty> class _Lambda_t {_Ty lexp;public:template<typename __Ty>
_Lambda_t(__Ty&&lexp):lexp(static_cast<__Ty&&>(lexp)){}template<typename... __Ty
>decltype(auto)operator()(__Ty&&...args){return lexp(std::ref(*this),static_cast
<__Ty&&>(args)...); } }; template <typename _Ty> decltype(auto) lexp(_Ty&&l_exp)
{ return _Lambda_t<typename std::decay<_Ty>::type>(static_cast<_Ty&&>(l_exp)); }
#ifdef XCODE
#define bg(...){cout<<"["<<__LINE__<<'@'<<++_LT[__LINE__]<<':';BG(__VA_ARGS__);}
size_t _LT[21777]; template<typename _Type>void BG(const _Type&_cur){cout<<' '<<
_cur << ']' << " <<:" << endl; } template<typename _Type,typename... _Other>void
BG(const _Type& _cur, const _Other& ..._other) {cout<< ' '<<_cur;BG(_other...);}
#else
#define bg(...)
#define dputs(...)
#endif

// :/

// :/

int STRUGGLING([[maybe_unused]] unsigned long long int TEST_NUMBER) {
   tp n; cin >> n;
   vetp a(n + 1, 0);
   for (tp i = 1; i <= n; ++i) cin >> a[i];
   auto calc = [&](tp k) -> tp {
      vetp cnt(n + 1, 0);
      tp jmp = 1, f = 0, lst = 1;
      for (tp i = 1; i <= n; ++i) {
         if (cnt[a[i]] != 0) continue;
         cnt[a[i]] = 1;
         ++f;
         if (f > k) {
            for (tp j = lst; j < i; ++j) cnt[a[j]] = 0;
            lst = i;
            ++jmp;
            f = 1;
         }
      }
      return jmp;
   };
   tp b = sqrt(n * log2(n));
   tp mav = 0, tar = -1;
   for (tp k = 1; k <= n; ++k) {
      if (k <= b) cout << calc(k) << " \n"[k == n];
      else if (k <= mav) cout << tar << " \n"[k == n];
      else {
         tp v = calc(k);
         tp l = k, r = n, tgt = k;
         while (l <= r) {
            tp mid = (l + r) / 2;
            if (calc(mid) < v) r = mid - 1;
            else {
               l = mid + 1;
               tgt = mid;
            }
         }
         cout << v << " \n"[k == n];
         mav = tgt;
         tar = v;
      }
   }
   return 0;
}

void MIST() {
   cin.tie(nullptr)->sync_with_stdio(false);
}

// :\ */

暂无评论

发表评论