2024初秋集训——提高组 #25

A. 数一下

题目描述

给定一个正整数 \(N\),求 \(N\bmod1,N\bmod2,\dots,N\bmod N\) 中有多少个不同的值。

思路

我们对 \(N\bmod i\)\(i\) 进行分类讨论:

  • \(i \ge \lceil\frac{N}{2}\rceil\),那么 \(N\bmod i=N-i\),所以这部分包含了 \(0\)\(\lfloor\frac{N}{2} \rfloor\)
  • \(i< \lceil\frac{N}{2}\rceil\),那么 \(N\bmod i < \lceil \frac{N}{2}\rceil-1\le \lfloor\frac{N}{2} \rfloor\),所以肯定也在该范围内。

综上,有 \(\lfloor \frac{N}{2}\rfloor+1\) 个不同的数。

代码

#include<bits/stdc++.h>
using namespace std;

int n;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n;
  cout << (n + 1) / 2;
  return 0;
}

B. 数两下

题目描述

给定一个数列,求将其分成若干非空子段使得每段 mex 相同的方案数。

思路

可以发现,这些段的 mex 如果要相同,那么只能等于整个数组的 mex。按这个 dp 即可。

时空复杂度均为 \(O(N)\)

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 300005, MOD = 998244353;

int n, a[MAXN], mex, dp[MAXN], sum[MAXN], Min[MAXN], last[MAXN], ans, pos[MAXN];
bool vis[MAXN];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
    vis[a[i]] = 1;
  }
  for(int i = 0; i <= n + 1; ++i) {
    pos[i] = -1;
    if(!vis[i]) {
      mex = i;
      break;
    }
  }
  last[0] = -1;
  multiset<int> s;
  for(int i = 0; i < mex; ++i) {
    s.insert(-1);
  }
  for(int i = 1; i <= n; ++i) {
    last[i] = last[i - 1];
    if(a[i] == mex) {
      last[i] = i;
    }
    if(a[i] < mex) {
      s.erase(s.find(pos[a[i]]));
      pos[a[i]] = i;
      s.insert(i);
    }
    Min[i] = -1;
    if(mex) {
      Min[i] = *s.begin();
    }
  }
  dp[0] = sum[0] = 1;
  for(int i = 1; i <= n; ++i) {
    dp[i] = (last[i] > Min[i] - 1 ? 0 : (sum[Min[i] - 1] - (last[i] < 0 ? 0 : sum[last[i]]) + MOD) % MOD);
    sum[i] = (sum[i - 1] + dp[i]) % MOD;
    Min[i] = min(Min[i], last[i]);
  }
  cout << dp[n];
  return 0;
}
posted @ 2024-09-28 21:27  Yaosicheng124  阅读(4)  评论(0编辑  收藏  举报