AT AGC003 题解
A
简单题,由于每一步的距离都可以随意确定,同时只要求最后回到原点,所以只要各个方向上都有相应相反的方向存在即为合法,反之存在一个不匹配的方向则不合法。我的写法绝对傻了。
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
std::string s;
std::cin >> s;
int tn = 0, ts = 0, te = 0, tw = 0;
for (auto i : s) {
if (i == 'N')
tn++;
if (i == 'S')
ts++;
if (i == 'E')
te++;
if (i == 'W')
tw++;
}
if (tn) {
if (ts) {}
else {
std::cout << "No\n";
return;
}
}
if (ts) {
if (tn) {}
else {
std::cout << "No\n";
return;
}
}
if (tw) {
if (te) {}
else {
std::cout << "No\n";
return;
}
}
if (te) {
if (tw) {}
else {
std::cout << "No\n";
return;
}
}
std::cout << "Yes\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
solve();
return 0;
}
B
简单贪心题,容易发现,每个选项要么和自己匹配,要么和相邻匹配,我们钦定每个位置和自己或者后一个位置匹配,显然和自己合并与和下一个数合并的次序和安排不影响答案,我们优先匹配自身,则 \(\bmod 2\) 之后只有 \(0/1\),如果 \(a_i = a_{i + 1} = 1\) 则都减去 \(1\) 之后累进答案。
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
i64 ans;
for (int i = 1; i <= n; i++) {
ans += a[i] / 2;
a[i] = a[i] % 2;
if (i != n && a[i] && a[i + 1]) {
a[i]--;
a[i + 1]--;
ans++;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
solve();
return 0;
}
C
注意审题。交换的是“相邻”的,同时“反转”顺序。容易发现操作一本质交换 \(a_i, a_{i + 1}\),操作二本质交换 \(a_i, a_{i + 2}\),下标奇偶发生了变化。最小化操作一就尽可能,直到不得不用操作二。那么我们排序,找到新的位置和原来位置,如果下标奇偶性不一样那么就要用一次操作一调整坐标,答案最后记得除以 \(2\)。
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1), b(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
b[i] = a[i];
}
std::sort(b.begin() + 1, b.end());
for (int i = 1; i <= n; i++) {
a[i] = std::distance(b.begin(), std::lower_bound(b.begin() + 1, b.end(), a[i]));
}
i64 ans = 0;
for (int i = 1; i <= n; i++) {
if ((a[i] & 1) != (i & 1))
ans++;
}
std::cout << (ans / 2) << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
solve();
return 0;
}
D
数论大分讨,但是很妙啊。
不难发现,对于一个立方数而言,每一种质因子的个数一定是 \(3\) 的倍数;同时普遍地,对于一个数 \(x\),最多只有一个 \(\gt \sqrt{x}\) 的质因子。设 \(s_i\) 上界为 \(L\),一个朴素的想法是去拆每一个 \(s_i\),检查是否有一个大于 \(\sqrt{L}\) 的质因子,如果有则无法和其他数相乘得到 cubic number,复杂度来到 \(O(n \sqrt{L})\),考虑怎么扩展这个做法:对于一个数 \(x\),最多只有两个大于 \(\sqrt[3]{x}\) 的质因子。
首先,我们将 \([1, \sqrt[3]{L}]\) 中 \(s_i\) 每个立方根因子排除掉,保证质因子个数控制在两个以内,找到每一个 \(s_i\) 每一个立方根因子剩下的具体个数。
对于数 \(x\) 进行分类讨论,将其每个质因子个数对 \(3\) 取模,产生冲突的数会是两两对应的(对 \(3\) 取差)。
- 有一个 \(\gt \sqrt{n}\) 的质因子,则不会和其他数产生冲突,直接累计进答案算贡献
- 有一个/两个大小在 \([\sqrt[3]{n}, \sqrt{n}]\) 之间的质因子,此时当且仅当两个质因子大小相同、个数分别为 \(1, 2\) 时才有可能会产生冲突。假设 \(x\) 含有的两个在此范围内的质因子大小不同,则无法和其他数产生冲突,直接计入贡献;如果只有一个或者两个大小相同的质因子,才有可能产生冲突,我们对在此范围内的质因子开
std::pair的std::map用于查询 - 不存在 \(\sqrt[3]{n}\) 以上质因子的 \(s_i\),只会冲突,我们还是开
std::map,对于当前的 \(s_i\),取模之后找到冲突的 \(x'\),将 \(s_i\) 所在项 \(+ 1\) 与 \(x'\) 所在项取 \(\max\) 即为键值对答案的贡献
复杂度降到了 \(n\sqrt[3]{n}\) 级别,加上一个 \(n \log n\) 的预处理。
#include <bits/stdc++.h>
#define int long long
constexpr int N = 1e5 + 7;
constexpr int SL1 = 1e5;
constexpr int SL2 = 2154;
int n, mxs, ans;
int pct1, pct2;
int s[N], pri[N], g[N];
bool isp[N];
std::map<int, int> t0;
std::map<std::pair<int, int>, int> t1, t2;
void init() {
for (int i = 2; i <= SL1; i++) {
if (!isp[i])
pri[++pct1] = i;
if (i == SL2)
pct2 = pct1;
for (int j = 1; j <= pct1 && i * pri[j] <= SL1; j++) {
isp[i * pri[j]] = 1;
if (i % pri[j] == 0)
break;
}
}
}
void solve(int x) {
memset(g, 0, sizeof(g));
int sum1 = 1, sum2 = 1;
for (int i = 1; i <= pct2; i++) {
while (x % pri[i] == 0) {
x /= pri[i];
g[i]++;
}
g[i] %= 3;
if (g[i] == 1) {
sum1 *= pri[i];
sum2 *= pri[i] * pri[i];
} else if (g[i] == 2) {
sum1 *= pri[i] * pri[i];
sum2 *= pri[i];
}
}
int p = std::sqrt(x);
if (SL2 < x && x <= SL1 && !isp[x]) {
int c1 = ++t1[{x, sum1}], c2 = t2[{x, sum2}];
if (c1 > c2)
ans++;
} else if (x > SL1 && p * p == x && !isp[p]) {
int c1 = ++t2[{p, sum1}], c2 = t1[{p, sum2}];
if (c1 > c2)
ans++;
} else if (x == 1) {
if (sum1 == 1 && sum2 == 1) {
if (!t0[sum2])
ans++;
t0[sum2] = 1;
return;
}
int c1 = ++t0[sum1], c2 = t0[sum2];
if (c1 > c2)
ans++;
} else {
ans++;
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
init();
std::cin >> n;
for (int i = 1; i <= n; i++) {
std::cin >> s[i];
}
for (int i = 1; i <= n; i++) {
solve(s[i]);
}
std::cout << ans << "\n";
return 0;
}
E
一个显然的发现时是,如果有两个操作 \(q_i, q_{i + 1}\) 相邻且 \(q_{i} \geq q_{i + 1}\),那么 \(q_i\) 是可以舍弃的,通过这样的重复操作将 \(q\) 压缩为一个单调递增的序列。考虑第 \(i\) 次操作时,之前的序列会被重复 \(\lfloor \frac{q_i}{q_{i - 1}} \rfloor\) 次,再加上一个 \(q_i \bmod q_{i - 1}\) 的部分。怎么处理这个多余的部分呢?这就是第 \(i - 1\) 次操作后序列的某个前缀,如果有一个操作 \(p\) 使得 \(x - 1\) 次操作后数列长度 \(\lt x\) 且第 \(p\) 次操作后数列长度 \(\geq x\),由于序列 \(q\) 单调递增,序列第 \(p\) 次操作后数列的前 \(x\) 项就是那块多余的部分,可以通过分治+二分求出具体的 \(p\)。差分添加贡献,倒推累计贡献。
#include <bits/stdc++.h>
#define int long long
constexpr int N = 1e5 + 7;
int n, m;
int f[N], d[N], q[N];
void solve(int x, int v) {
int p = std::lower_bound(q + 1, q + m + 1, x) - q - 1;
if (p == 0) { d[1] += v; d[x + 1] -= v; }
else {
f[p] += (x / q[p]) * v;
solve(x % q[p], v);
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
int c = 1; q[1] = n;
for (int i = 1, x; i <= m; i++) {
std::cin >> x;
while (c && q[c] > x)
c--;
q[++c] = x;
}
m = c; f[m] = 1;
for (int i = m; i; i--) {
solve(q[i], f[i]);
}
for (int i = 1; i <= n; i++) {
d[i] += d[i - 1];
std::cout << d[i] << "\n";
}
return 0;
}
F
注意到题目保证 # 上下联通,分类讨论,处理合并时的边界情况,矩阵转移计算贡献。
#include <bits/stdc++.h>
using i64 = long long;
constexpr int P = 1e9 + 7;
struct Matrix {
int n, m;
std::vector<std::vector<i64>> a;
Matrix(int n_ = 0, int m_ = 0) : n(n_), m(m_) {
a.assign(n + 1, std::vector<i64>(m + 1, 0));
}
Matrix operator * (const Matrix& rhs) const {
Matrix ret(n, rhs.m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= rhs.m; j++) {
for (int k = 1; k <= m; k++) {
ret.a[i][j] = (ret.a[i][j] + a[i][k] * rhs.a[k][j] % P) % P;
}
ret.a[i][j] = (ret.a[i][j] + P) % P;
}
}
return ret;
}
};
Matrix fpow(Matrix A, i64 k) {
Matrix ret(A.n, A.m);
for (int i = 1; i <= ret.n; i++) {
ret.a[i][i] = 1;
}
while (k) {
if (k & 1) ret = ret * A;
A = A * A;
k >>= 1;
}
return ret;
}
int fpow(int a, i64 k) {
int ret = 1;
while (k) {
if (k & 1) ret = 1LL * ret * a % P;
a = 1LL * a * a % P;
k >>= 1;
}
return ret;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
i64 k;
std::cin >> n >> m >> k;
std::vector<std::string> mp(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> mp[i];
mp[i] = " " + mp[i];
}
int a = 0;
std::vector<int> b(2), c(2);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (mp[i][j] == '#') {
a++;
if (j > 1) b[0] += (mp[i][j - 1] == '#');
if (i > 1) b[1] += (mp[i - 1][j] == '#');
}
}
}
for (int i = 1; i <= n; i++) {
c[0] += (mp[i][1] == '#' && mp[i][m] == '#');
}
for (int i = 1; i <= m; i++) {
c[1] += (mp[1][i] == '#' && mp[n][i] == '#');
}
if (c[0] && c[1]) {
std::cout << "1\n";
return 0;
}
if (!c[0] && !c[1]) {
std::cout << fpow(a, k - 1) << "\n";
return 0;
}
int tag = !!c[1];
Matrix A(2, 2), I(2, 2), B(2, 1);
for (int i = 1; i <= 2; i++) {
I.a[i][i] = 1;
}
A.a[1][1] = a;
A.a[1][2] = -b[tag];
A.a[2][1] = 0;
A.a[2][2] = c[tag];
B.a[1][1] = B.a[2][1] = 1;
B = fpow(A, k - 1) * B;
std::cout << B.a[1][1] << "\n";
return 0;
}

浙公网安备 33010602011771号