20250823
T1
看到第一眼,合并芒果?不对,这是 \(a \times b\),而且题目中还有取模,哪里有求最小值,还要取模的?最小值不会变么?
这启示我们,也许怎么合并都是一样的。手模/打一个暴力,你会发现确实是这样,那么第一问就好解决了,第二问怎么做?
很简单,每一次合并芒果时都会在总数里选两个芒果合并,这个直接乘法原理组合数计算就行了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7, kN = 1e5 + 7;
int T, n, op, ans1, a[kN], ans2;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
for (cin >> T; T--;) {
cin >> n >> op, ans1 = 0, ans2 = 1;
if (n == 1) ans2 = 0;
fill(a + 1, a + (n << 1) + 3, 0);
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (i - 1) (ans2 *= 1ll * i * (i - 1) / 2 % mod) %= mod;
}
for (int i = 1, r = n; i <= r; i += 2, r++) {
if (a[i] == 0 || a[i + 1] == 0) break;
a[r + 1] = (a[i] + a[i + 1]) % mod, (ans1 += a[i] * a[i + 1] % mod) %= mod;
}
cout << ans1 << ' ' << op * ans2 << '\n';
}
return 0;
}
T2
看了很久 T3,没时间写了。
我们发现亲密度越大时,对数一定越多。那么这就可以二分出小于 \(k\) 对的最大亲密值。
check 可以写二分,也可以写双指针,首先不考虑所有相同颜色的人,假设现在的亲密值需要小于等于 \(x\),那么就相当于一个长度为 \(x\) 的滑动窗口,在长度为 \(n\) 的序列中滑动,方案数是 \(n \times (n - x)\),并且在最后还要加上 \(x\) 之中的对数,是 \(\dfrac{x(x-1)}{2}\),最后处理所有相同颜色的,这个可以将所有相同颜色的人放进同一个 vector 中,然后由于下标有单调性,双指针处理。
最后我们得到了一个等于 \(k\) 的最大亲密值,扫一遍所有亲密值为最大 \(x\) 的即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int kN = 1e5 + 7;
int n, k, a[kN], mx = 0, ans, p;
vector<int> g[kN];
// 在亲密度为 <= x 时有几对
int calc(int x) {
int res = x * (n - x) + x * (x - 1) / 2;
for (int i = 1; i <= n; i++) {
int m = g[i].size();
for (int l = 0, r = 0; r < m; r++) {
while (l < m && g[i][r] - g[i][l] > x) l++;
res -= r - l;
}
}
return res;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i], g[a[i]].push_back(i);
}
// 对数严格小于 k 的最大亲密度.
if (calc(n) < k) return cout << -1, 0;
for (int l = 1, r = n - 1, mid; l <= r;) {
mid = (l + r) >> 1;
if (calc(mid) < k)
l = mid + 1, mx = mid;
else
r = mid - 1;
}
ans = calc(mx), mx++;
for (int i = 1; i <= n - mx; i++) {
if (a[i] != a[i + mx]) ans++;
if (ans == k) return cout << i << ' ' << i + mx, 0;
}
return 0;
}
T3
看了好久没看出来
两个定理:
唯一分解定理:\(\begin{aligned}n=\prod^k_{i=1}\end{aligned}a_i^{p_i}\),正约数和定理:\(\begin{aligned}S=\prod^k_{i=1}\sum^{p_i}_{j=0}a_i^j\end{aligned}\)。
由此可以通过拆分数 \(n\) 做,每一次除去一个数 \(a_i^{q_i}\),此时\(0 \le q_i \le p_i\),然后将 \(sum\) 乘上 \(\sum_{j=0}^{q_j}a_i^j\)。最后如果拆完了,或者剩下一个极大质因子,那么答案加 \(1\),但是注意需要去重,见 \(n=684\),有可能最后拆出不同方案,因此需要保证这个极大质因数大于 \(\sqrt{2\times 10^9}\)。
这个过程可以爆搜,看似不正确,实则因为质因数个数极少,可以通过。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int kN = 1e6 + 7;
int T, n, g[kN], tot;
bool b[kN];
vector<int> r;
void Prime() {
for (int i = 2; i < kN; i++) {
if (b[i] == 0) g[++tot] = i;
for (int j = 1; j <= tot; j++) {
if (i * g[j] >= kN) break;
b[i * g[j]] = 1;
if (i % g[j] == 0) break;
}
}
}
bool isp(int x) {
if (x <= 1) return 0;
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) return 0;
}
return 1;
}
void DFS(int now, int x, int sum) {
if (now == 1) return r.push_back(sum);
if (isp(now - 1) && now > g[x]) r.push_back(sum * (now - 1));
for (int i = x; g[i] * g[i] <= now; i++) {
for (int j = g[i] + 1, val = g[i]; j <= now; val *= g[i], j += val) {
if (now % j == 0) DFS(now / j, i + 1, sum * val);
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
Prime();
for (cin >> T; T--;) {
cin >> n, r.clear(), DFS(n, 1, 1);
sort(r.begin(), r.end());
cout << r.size() << '\n';
for (int x : r) cout << x << ' ';
if (r.size()) cout << '\n';
}
return 0;
}
T4
我不会 FHQ 啊!所以我打了个 \(O(n^2)\) 的暴力水过去了!
设在前 \(i\) 位上升子序列中 \(a_i\) 的最小值为 \(f_i\),当 \(f_{i-1} < l\) 时,有 \(f_i = \min\{l,f_i\}\),当 \(l \le f_{i-1} < r\) 时,有 \(f_i = f_{i-1} + 1\),当 \(f_{i-1} \ge r\) 时,不可能转移。
\(f_0=0\),其他都为极大值,最后答案就是在所有不为极大值的 \(f_i\) 中取最大。
这个显然是 \(O(n^2)\) 的,由于有单调性,上面的转移形式可以用平衡树维护,但我不会啊,我直接卡时就过了。
#include <bits/stdc++.h>
using namespace std;
const int kN = 3e5 + 7;
int n, l[kN], r[kN], f[kN], ans;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> l[i] >> r[i];
memset(f, 0x7f, sizeof f), f[0] = 0;
int s = 0;
for (int i = 1; i <= n; i++) {
s = 0;
for (int j = i; j; j--) {
s++;
if (f[j - 1] < l[i]) f[j] = min(f[j], l[i]);
if (l[i] <= f[j - 1] && f[j - 1] < r[i]) f[j] = min(f[j], f[j - 1] + 1);
if (s * n > 8e7) break;
}
}
for (int i = 1; i <= n; i++) {
ans += (f[i] < 1e9);
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号