AtCoder Regular Contest 192 (Div. 2)

ARC192A ARC Arc

把需要的状态全记下来,然后直接 DP。

constexpr int N = 2e5 + 5;
bool f[N][3][3][4];

void slv() {
	int n = Read<int>();
	
	vector<int> A(n);
	for (int i = 0; i < n; i ++) {
		Read(A[i]);
	}
	
	auto chk = [&](int x, int y, int z) -> bool {
		if (x == 0 && y == 1 && z == 2) {
			return true;
		}
		if (x == 2 && y == 1 && z == 0) {
			return true;
		}
		return false;
	};
	for (int A0 = 0; A0 < 3; A0 ++) {
		for (int A1 = 0; A1 < 3; A1 ++) {
			memset(f, false, sizeof f);
			f[1][A0][A1][1] = true;
			for (int i = 1; i + 1 < n; i ++) {
				for (int x = 0; x < 3; x ++) {
					for (int y = 0; y < 3; y ++) {
						for (int o = 0; o < 4; o ++) {
							if (f[i][x][y][o]) {
								int k = o >> 1;
								for (int z = 0; z < 3; z ++) {
									bool can = chk(x, y, z);
									if (can) {
										if (i == 1) {
											f[i + 1][y][z][3] = true;
										} else {
											f[i + 1][y][z][k << 1 | 1] = true;
										}
									} else if (o & 1) {
										f[i + 1][y][z][k << 1 | A[i]] = true;
									}
								}
							}
						}
					}
				}
			}
			for (int x = 0; x < 3; x ++) {
				for (int y = 0; y < 3; y ++) {
					for (int o = 0; o < 4; o ++) {
						if (f[n - 1][x][y][o]) {
							bool can = true;
							int k = o >> 1, m = o & 1;
							can &= m || chk(x, y, A0);
							can &= A[n - 1] || chk(x, y, A0) || chk(y, A0, A1);
							can &= k || A[0] || chk(y, A0, A1);
							if (can) {
								Yes();
								return;
							}
						}
					}
				}
			}
		}
	}
	No();
	return;
}

ARC192B Fennec VS. Snuke 2

发现只有奇数的有用,然后打表。

怎么看见博弈论只会打表

void slv() {
	int n = Read<int>();
	int c1 = 0;
	for (int i = 0; i < n; i ++) {
		c1 += Read<int>() & 1;
	}
	if (n == 1) {
		Puts("Fennec");
	} else if (n == 2) {
		Puts("Snuke");
	} else if (n == 3) {
		if (!c1) {
			Puts("Snuke");
		} else {
			Puts("Fennec");
		}
	} else {
		if (c1 & 1) {
			Puts("Fennec");
		} else {
			Puts("Snuke");
		}
	}
	return;
}

ARC192C Range Sums 2

由于 \(A_i > 0\),所以我们随便找一个点问一下它和其他每个点,这样最大的那个位置就一定是端点,这里花费 \(n - 1\) 次。

从这个端点再做一遍和上面相同的事情,那么我们问到的就是所有的前缀 / 后缀和,这样排序后就能得到另一个端点,还有正序或反序的 \(A\)\(P\),花费 \(n - 2\) 次。

我们还需要知道两个端点分别是谁,由于 \(P_1 < P_2\),所以只需要问一下和 \(P_1, P_2\) 分别的区间和即可,这部分不消耗询问。

最后的问题是我们现在还没有确定 \(a_0, a_1\)(或 \(a_{n - 1}, a_n\))的值,这个只需要询问一下 \(a_0 + a_1 + a_2\)\(a_1 +a_2\)(或 \(a_{n - 2} + a_{n - 1} + a_n\)\(a_{n - 2} + a_{n - 1}\))即可,这部分消耗 \(1\) 次询问。

总询问数 \(2n - 2\)

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	
	int n;
	cin >> n;

	map<pair<int, int>, i64> mp;
	auto qry = [&](int s, int t) {
		if (s == t) {
			return 0LL;
		}
		if (s > t) {
			swap(s, t);
		}
		if (mp.contains({s, t})) {
			return mp[{s, t}];
		}
		cout << "? " << s + 1 << ' ' << t + 1 << endl;
		i64 sum; cin >> sum;
		return mp[{s, t}] = sum;
	};
	auto ans = [&](vector<int> p, vector<int> a) {
		cout << "! ";
		for (auto x : p) {
			cout << x + 1 << ' ';
		}
		for (auto x : a) {
			cout << x << ' ';
		}
		cout << endl;
		return;
	};
	
	int p0 = 0;
	vector<int> p(n), a(n);
	for (int i = 1; i < n; i ++) {
		if (qry(0, i) > qry(0, p0)) {
			p0 = i;
		}
	}
	
	vector<pair<i64, int>> sum(n);
	for (int i = 0; i < n; i ++) {
		sum[i] = {qry(p0, i), i};
	}
	int p1 = max_element(sum.begin(), sum.end()) - sum.begin();
	sort(sum.begin(), sum.end());
	for (int i = n - 1; i > 1; i --) {
		a[i] = sum[i].first - sum[i - 1].first;
	}
	a[0] = qry(p0, sum[2].second) - qry(sum[1].second, sum[2].second);
	a[1] = qry(p0, sum[1].second) - a[0];
	
	if (p0 == 0 || (p0 != 1 && qry(p0, 0) < qry(p0, 1))) {
		p[p0] = 0, p[p1] = n - 1;
		for (int i = 1; i + 1 < n; i ++) {
			p[sum[i].second] = i;
		}
	} else {
		p[p1] = 0, p[p0] = n - 1;
		for (int i = 1; i + 1 < n; i ++) {
			p[sum[i].second] = n - i - 1;
		}
		reverse(a.begin(), a.end());
	}
	
	ans(p, a);
	
	return 0;
}

ARC192D Fraction Line

首先可以发现 \(\displaystyle f(x) = \frac{\operatorname{lcm}(P, Q)}{\gcd(P, Q)}\)

每个质因子独立,对每个质因子分别计算答案,最后乘起来即可。

记质数 \(p\)\(A_i\) 中的次数为 \(B_i\),那么我们要做的就是:

对于一个序列 \(C\),它的权值为 \(\displaystyle \prod_{1 \le i \le n} p^{C_i}\)
求所有满足:

  • \(\forall 1 \le i < n, \max(C_i, C_{i + 1}) - \min(C_i, C_{i + 1}) = B_i\)
  • \(\exists 1 \le i \le n, C_i = 0\)
    的序列 \(C\) 的权值之和。

这个问题可以简单地通过 \(O\big(n \sum B \big)\) 的 DP 解决。

那么总时间复杂度就是 \(O\big(n^2 \log \max A\big)\) 的。

void slv() {
	int n = Read<int>();
	
	vector<int> A(-- n);
	for (int i = 0; i < n; i ++) {
		Read(A[i]);
	}
	
	mint ans = mint::raw(1);
	for (int p = 2; p <= 1000; p ++) {
		if ([&]() -> bool {
			for (int i = 2; i * i <= p; i ++) {
				if (p % i == 0) {
					return true;
				}
			}
			return false;
		}()) {
			continue;
		}
		
		vector<int> B(n);
		for (int i = 0; i < n; i ++) {
			while (A[i] % p == 0) {
				A[i] /= p, ++ B[i];
			}
		}
		
		const int m = accumulate(B.begin(), B.end(), 0);
		vector<array<mint, 2>> f(m + 1);
		vector<mint> pw(m + 1);
		pw[0] = mint::raw(1);
		for (int i = 1; i <= m; i ++) {
			pw[i] = pw[i - 1] * p;
		}
		for (int i = 0; i <= m; i ++) {
			f[i][!i] = pw[i];
			f[i][!!i] = mint::raw(0);
		}
		for (int i = 0; i < n; i ++) {
			auto g = f;
			fill(f.begin(), f.end(), array<mint, 2>{0, 0});
			for (int j = 0; j <= m; j ++) {
				mint g0 = 0, g1 = 0;
				if (j - B[i] >= 0) {
					g0 += g[j - B[i]][0];
					g1 += g[j - B[i]][1];
				}
				if (B[i] && j + B[i] <= m) {
					g0 += g[j + B[i]][0];
					g1 += g[j + B[i]][1];
				}
				f[j][!j] += g0 * pw[j];
				f[j][1] += g1 * pw[j];
			}
		}
		
		mint res = 0;
		for (int i = 0; i <= m; i ++) {
			res += f[i][1];
		}
		ans *= res;
	}
	
	Write((int)ans, '\n');
	
	return;
}

ARC192E Snuke's Kyoto Trip

这个题真的有 B 难吗,怎么这种唐题放 E 啊 /lh

感觉是史,开摆了。

posted @ 2025-03-13 10:55  definieren  阅读(39)  评论(0)    收藏  举报