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;
}