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::pairstd::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;
}
posted @ 2025-11-17 21:53  夢回路  阅读(5)  评论(0)    收藏  举报