好题选讲3
G HUGE GCD SPOJ18202
给定两个长度分别为 \(n\) 和 \(m\) 的序列 \(a\) 和 \(b\),求 \(\gcd(\prod\limits^n_{i = 1}a_i, \prod\limits^m_{i = 1}b_i)\)。\(n, m \leq 1000\)。
对每个 \(a\ O(n)\) 算贡献即可。时间复杂度 \(O(n^2)\)。
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 1000 + 1, kP = 1e9;
int n, m;
int a[kN], b[kN];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
cin >> m;
for (int i = 1; i <= m; i++) {
cin >> b[i];
}
int ans = 1, t = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1, g; j <= m; j++) {
g = __gcd(a[i], b[j]);
a[i] /= g, b[j] /= g;
t |= (1ll * ans * g >= kP);
ans = 1ll * ans * g % kP;
}
}
if (t) {
string s;
for (int i = 1; i <= 9; i++) {
s += ans % 10 + '0', ans /= 10;
}
reverse(s.begin(), s.end());
cout << s << '\n';
} else {
cout << ans << '\n';
}
return 0;
}
H MUH and Cube Walls CF471D
给两堵墙 \(a,b\),问 \(a\) 墙中与 \(b\) 墙顶部形状相同的区间有多少个。\(|a|,|b|\leq 2\times10^5, 1\leq a_i, b_i\leq 10^9\)。
这个形状相同不考虑高度,所以可以差分消除高度对区间形态判断的影响。然后字符串匹配即可。时间复杂度 \(O(n\log n)\)。
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2e5 + 1;
int n, m;
int a[kN], b[2 * kN];
int f[kN];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
if (m == 1) {
cout << n << '\n';
return 0;
}
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
cin >> b[i];
}
for (int i = 1; i < n; i++) {
a[i] -= a[i + 1];
b[i] -= b[i + 1];
}
b[m] = -2e9, n--;
copy_n(a + 1, n, b + m + 1);
int ans = 0;
for (int i = 2, j = 0; i <= n + m; i++) {
for (; j && b[j + 1] != b[i]; j = f[j]) {
}
b[j + 1] == b[i] && j++;
f[i] = j;
j == m - 1 && ans++;
}
cout << ans << '\n';
return 0;
}
I Increase Sequence CF466D
给定一个长度为 \(n\) 的序列,可以对任意个区间 \([l, r]\) 内的数 \(+1\),同一个点不能同时作为两个区间的左端点或右端点。问使这个序列每个数全部变为 \(h\) 的方案数\(\mod 10^9+7\)。\(1\leq n, h\leq 2000, 0\leq a_i \leq 2000\)。
变为 \(h\) 很烦,不如直接让 \(a_i = h - a_i\) 然后区间 \(-1\) 序列消为 \(0\)。
区间 \(-1\) 也很烦,不如直接将 \(a\) 差分为 \(d\) 把区间问题转化为每次一个点 \(-1\) 后面一个点 \(+1\) 序列消为 \(0\)。注意要差分要补上第 \(n+1\) 项。(因为这项是有意义的,区间减到最后一个数他就要加的)
什么情况无解?当差分后 \(\exist 1\leq i \leq n + 1, |d_i| > 1\),因为不能同时减两次,所以无解。不存在其他无解条件。
于是分类讨论 \(d_i\) 的值。令前 \(i - 1\) 项有 \(cnt\) 个区间没有遇到右端点以闭合,则:
- 若 \(d_i = 0\),在 \(i\) 上,可以什么也不干(\(1\) 种方案),也可以同时作为左端点和右端点(\(cnt\) 种方案),共 \(cnt+1\) 种。
- 若 \(d_i = 1\),在 \(i\) 上,只能新开一个区间,方案数不变,cnt++
- 若 \(d_i = -1\),在 \(i\) 上,只能作为右端点让一个区间闭合(\(cnt\) 种方案),同时 cnt--
做完了,时间复杂度 \(O(n)\)。不知道这个数据范围有没有骗到人。
Code
// STOOOOOOOOOOOOOOOOOOOOOOOOO hzt CCCCCCCCCCCCCCCCCCCCCCCORZ
#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
constexpr int kN = 2e3 + 2, kP = 1e9 + 7;
int n, h;
int a[kN];
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> h;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i] = h - a[i];
}
for (int i = n + 1; i >= 1; i--) {
a[i] -= a[i - 1];
if (abs(a[i]) > 1) {
cout << "0\n";
return 0;
}
}
int cnt = 0, ans = 1;
for (int i = 1; i <= n; i++) {
if (a[i] == 1) {
cnt++;
} else if (a[i] == 0) {
ans = ans * (cnt + 1ll) % kP;
} else {
ans = 1ll * ans * cnt % kP, cnt--;
}
}
cout << ans << '\n';
return 0;
}
J Equation ARC158D
给定一个不小于 \(5\) 的质数 \(p\) 和一个正整数 \(n\),求一个三元组 \((a,b,c)\) 使 \((a+b+c)(a^n+b^n+c^n)(a^{2n}+b^{2n}+c^{2n})\equiv a^{3n}+b^{3n}+c^{3n}\ (mod\ p)\)。\(1\leq a<b<c<p, n, p\leq 10^9\),\(10^5\) 组数据。
随便一个三元组。只要同余式两边不是 \(0\),总存在一个数 \(k\) 使得
因为可以直接除。
两边同时除以 \(k^{3n+1}\) 有
于是获得了一组解。但是有可能出现两个数相同得情况,需要多随几遍。
Code
代码太丑了,可以看 bykem 的。