AtCoder Grand Contest 006
题目传送门:AtCoder Grand Contest 006。
A - Prefix and Suffix
#include <iostream>
#include <string>
int N;
std::string s, t;
int main() {
std::cin >> N >> s >> t;
for (int i = N; i >= 0; --i)
if (s.substr(N - i) == t.substr(0, i))
return std::cout << N + N - i, 0;
}
B - Median Pyramid Easy
我们把 \(x\) 看作 \(0\),把 \(< x\) 的数看作 \(-1\),把 \(> x\) 的数看作 \(1\)。
如果 \(x = 1\) 或 \(2 N - 1\) 容易发现无法构造,否则考虑 \(-1, 0, 1\) 相邻时会给出一个 \(0\);两个 \(0\) 相邻时就永远都是 \(0\)。
考虑在正中心位置构造 -+0-
或 +-0+
即可。
#include <cstdio>
int N, x;
int main() {
scanf("%d%d", &N, &x);
if (x == 1 || x == 2 * N - 1) return puts("No"), 0;
puts("Yes");
if (N == 2) return puts("1\n2\n3"), 0;
if (x == 2) {
for (int i = 1; i <= 2 * N - 1; ++i)
printf("%d\n", (N + i) % (2 * N - 1) + 1);
return 0;
}
int c = 0;
for (int i = 1; i <= N - 3; ++i) {
for (++c; x - 2 <= c && c <= x + 1; ++c) ;
printf("%d\n", c);
}
printf("%d\n%d\n%d\n%d\n", x - 2, x + 1, x, x - 1);
for (int i = 1; i <= N - 2; ++i) {
for (++c; x - 2 <= c && c <= x + 1; ++c) ;
printf("%d\n", c);
}
return 0;
}
C - Rabbit Exercise
根据期望的线性性,每次即是 \(x_a \gets x_{a - 1} - 2 x_a + x_{a + 1}\)。
考虑 \(x\) 的差分 \(y\),容易发现变换后 \(y_a\) 与 \(y_{a + 1}\) 互换了。我们仅考虑 \(y\),则 \(M\) 次操作后即是对 \(y\) 的一个置换。
做置换的 \(K\) 次幂即可得到最终的 \(y\),前缀和还原回 \(x\)。
#include <cstdio>
#include <algorithm>
typedef long long LL;
const int MN = 100005;
int N, M; LL K;
LL x[MN];
int per[MN], vis[MN], stk[MN], tp;
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; ++i) scanf("%lld", &x[i]);
for (int i = N; i >= 2; --i) x[i] -= x[i - 1];
for (int i = 1; i <= N; ++i) per[i] = i;
scanf("%d%lld", &M, &K);
while (M--) {
int p;
scanf("%d", &p);
std::swap(per[p], per[p + 1]);
}
for (int i = 1; i <= N; ++i) if (!vis[i]) {
tp = 0;
int u = i;
do vis[u] = 1, stk[tp++] = u, u = per[u]; while (u != i);
for (int j = 0; j < tp; ++j) per[stk[j]] = stk[(j + K) % tp];
}
LL s = 0;
for (int i = 1; i <= N; ++i) s += x[per[i]], printf("%lld\n", s);
return 0;
}
D - Median Pyramid Hard
沿着 B 的思路继续,考虑判断答案是否大于等于某个值 \(x\):把 \(\ge x\) 的都看作 \(1\),\(< x\) 都看作 \(0\)。
手动模拟即可发现就是要求中心往两侧走到达的第一个相邻相同的位置,哪边更近哪边就会先靠过来。
#include <cstdio>
const int MN = 200005;
int N, M, A[MN];
inline bool check(int x) {
int l = 1, r = M;
for (int i = N; i > 1; --i)
if ((A[i] >= x) == (A[i - 1] >= x)) { l = i; break; }
for (int i = N; i < M; ++i)
if ((A[i] >= x) == (A[i + 1] >= x)) { r = i; break; }
return N - l <= r - N ? A[l] >= x : A[r] >= x;
}
int main() {
scanf("%d", &N), M = 2 * N - 1;
for (int i = 1; i <= M; ++i) scanf("%d", &A[i]);
int l = 3, r = M - 1, mid, ans = 2;
while (l <= r) {
mid = (l + r) / 2;
if (check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%d\n", ans);
return 0;
}
E - Rotate 3x3
注意到这个操作对每一列内部顺序是没有影响的(有可能翻转),先把这个判掉。
得到每一列分别是原来的哪一列变换来的,有没有翻转。
操作相当于是每次交换距离为 \(2\) 的两列,并且把它们都翻转,把中间那一列也翻转。
显然编号为奇数的列最终必须还在奇数位,偶数同理,这个也判掉。
再考虑奇数序列的逆序对,每次操作要么翻转 \(1\) 次,偶数序列逆序对改变 \(1\);要么逆序对改变 \(1\),偶数序列翻转 \(1\) 次。
所以说,逆序对数,和奇偶性不同的那个序列中的翻转次数,奇偶性应该是相同的。
再把这个判掉就 AC 了。
#include <cstdio>
const int MN = 100005;
int N, A[3][MN], B[MN];
int bit[MN];
inline void Add(int i) { for (; i; i -= i & -i) bit[i] ^= 1; }
inline int Qur(int i) { int s = 0; for (; i <= N; i += i & -i) s ^= bit[i]; return s; }
int main() {
scanf("%d", &N);
for (int j = 0; j < 3; ++j)
for (int i = 1; i <= N; ++i)
scanf("%d", &A[j][i]);
int p1 = 0, p2 = 0;
for (int i = 1; i <= N; ++i) {
if (A[1][i] % 3 != 2 || A[0][i] + A[2][i] != 2 * A[1][i]) return puts("No"), 0;
int x = A[1][i] / 3 + 1;
if ((A[0][i] + 2) / 3 != x || ((B[i] = x) ^ i) & 1) return puts("No"), 0;
if (A[0][i] == x * 3) (i & 1 ? p2 : p1) ^= 1;
}
for (int i = 1; i <= N; i += 2)
p1 ^= Qur(B[i]), Add(B[i]);
for (int i = 1; i <= N; ++i) bit[i] = 0;
for (int i = 2; i <= N; i += 2)
p2 ^= Qur(B[i]), Add(B[i]);
puts(p1 || p2 ? "No" : "Yes");
return 0;
}
F - Blackout
显然是个图论问题。考虑一条链 \(1 \to 2 \to 3 \to \cdots \to (n - 1) \to n\)。它最终会是什么样子的呢?
可以发现最终任意满足 \(x + 1 \equiv y \pmod{3}\) 的两点 \(x, y\),就会有 \(x \to y\) 的边,这启发我们使用三染色。
考虑每个弱连通分量,对其施加三染色,正向边颜色编号 \(+1\),反向边颜色编号 \(-1\)(模 \(3\) 意义下)。
如果三染色失败,说明该连通分量最终会变成任意两点之间都有双向边,每个点都有自环的完全图。
如果只用到了两种颜色,显然无法进行任何操作。
如果用到了三种颜色,则令颜色为 \(i\) 的节点的个数为 \(c_i\),则其为答案贡献 \(c_0 c_1 + c_1 c_2 + c_2 c_0\)。
#include <cstdio>
#include <vector>
typedef long long LL;
const int MN = 100005;
int N, M; LL Ans;
std::vector<int> G[MN], iG[MN];
int ok, col[MN], a[3], cv, ce;
void DFS(int u, int c) {
++a[col[u] = c], ++cv;
ce += G[u].size();
for (int v : G[u])
if (!~col[v]) DFS(v, (c + 1) % 3);
else if (col[v] != (c + 1) % 3) ok = 0;
for (int v : iG[u])
if (!~col[v]) DFS(v, (c + 2) % 3);
else if (col[v] != (c + 2) % 3) ok = 0;
}
int main() {
scanf("%d%d", &N, &M);
while (M--) {
int x, y;
scanf("%d%d", &x, &y);
G[x].push_back(y);
iG[y].push_back(x);
}
for (int i = 1; i <= N; ++i) col[i] = -1;
for (int i = 1; i <= N; ++i) if (!~col[i]) {
ok = 1, a[0] = a[1] = a[2] = cv = ce = 0;
DFS(i, 0);
if (!ok) Ans += (LL)cv * cv;
else if (a[0] && a[1] && a[2]) Ans += (LL)a[0] * a[1] + (LL)a[1] * a[2] + (LL)a[2] * a[0];
else Ans += ce;
}
printf("%lld\n", Ans);
return 0;
}