AGC006 题解
A - Prefix and Suffix
字符串长度最小等价于两个字符串拼接后的“重叠部分”最长,枚举
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
int main() {
string s, t;
int n, i;
cin >> n >> s >> t;
for(i = n; i >= 1; i--) {
if(s.substr(n - i, i) == t.substr(0, i)) {
break;
}
}
Write(2 * n - i);
return 0;
}
B - Median Pyramid Easy
先考虑将
一个直观的想法是在其左右分别放置一个小于
我们会惊喜地发现,只要满足上述条件就一定能把
证明:考虑
左上方的格子,设 左边的格子里的数为 , 左边的格子里的数为 ,分类讨论:
,则中位数 ; ,则中位数 ; ,则中位数 。 可以发现无论怎样,第
列的数一定 ,而同理可得第 列的数一定 ,则一定保证 能被传送上去。
所以任选
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 200005;
int a[N];
void Out(int n) {
printf("Yes\n");
int i;
for(i = 1; i < 2 * n; i++) {
Write(a[i]), putchar('\n');
}
}
int main() {
int n = Read(), x = Read(), i;
if(x == 1 || x == 2 * n - 1) {
printf("No");
return 0;
}
for(i = 1; i < 2 * n; i++) {
a[i] = i;
}
if(x == n) {
Out(n);
}
else if(x == n - 1) {
swap(a[x], a[x + 1]), swap(a[x - 1], a[x + 2]), Out(n);
}
else if(x == n + 1) {
swap(a[x], a[x - 1]), swap(a[x + 1], a[x - 2]), Out(n);
}
else {
swap(a[x], a[n]), swap(a[x - 1], a[n + 1]), swap(a[x + 1], a[n - 1]), Out(n);
}
return 0;
}
C - Rabbit Exercise
考虑每一次跳跃,设当前第
因此每一次操作等价为,给定
考虑差分,依旧设当前第
对于一轮操作,我们维护操作后每个位置上对应原来的哪个值,倍增即可得到
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 100005;
int n, m, a[N], id[64][N];
ll k, x[N], y[N];
int main() {
int i, j;
n = Read();
for(i = 1; i <= n; i++) {
x[i] = Read(), id[0][i] = i;
}
m = Read(), k = Read();
for(i = 1; i <= m; i++) {
a[i] = Read(), swap(id[0][a[i]], id[0][a[i] + 1]);
}
for(i = 1; i <= 62; i++) {
for(j = 1; j <= n; j++) {
id[i][j] = id[i - 1][id[i - 1][j]];
}
}
for(i = n; i; i--) {
x[i] -= x[i - 1];
}
for(i = 1; i <= n; i++) {
int t = i;
for(j = 62; j >= 0; j--) {
if((k >> j) & 1) {
t = id[j][t];
}
}
y[i] = x[t];
}
for(i = 1; i <= n; i++) {
y[i] += y[i - 1];
Write(y[i]), putchar('\n');
}
return 0;
}
D - Median Pyramid Hard
直接优化看起来没什么前途,注意到对于任意的集合
考虑二分答案
考虑上图在第一层有一组相邻的黑格和一组相邻的白格,可以直观的看出,两个黑格与两个白格一直斜着“传送”上去,在中间某一层“相遇”了,然后黑白格就有了一条分界线。因为黑格距离中间近,所以最上面的格子是黑色。
所以说我们直接找离中间最近的相邻两个格子就行了,显然的不可能有离中心一样近的两个格子。
还有一种情况,就是不存在任何一个离中心一样近的格子,模一下就会发现与最外面的格子颜色相同,于是就可以愉快的二分了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') {
sig = -1;
}
c = getchar();
}
while(isdigit(c)) {
num = (num << 3) + (num << 1) + (c ^ 48);
c = getchar();
}
return num * sig;
}
void Write(ll x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x >= 10) {
Write(x / 10);
}
putchar((x % 10) ^ 48);
}
const int N = 100005;
int n, p[N << 1];
bool Check(int x, int y) {
return p[x] >= y && p[x + 1] >= y;
}
bool Check2(int x, int y) {
return p[x] < y && p[x + 1] < y;
}
bool Solve(int x) {
int i;
for(i = 0; i < n; i++) {
if(Check(n - i - 1, x) || Check(n + i, x)) {
return true;
}
if(Check2(n - i - 1, x) || Check2(n + i, x)) {
return false;
}
}
return p[1] >= x;
}
int main() {
int i;
n = Read();
for(i = 1; i < (n << 1); i++) {
p[i] = Read();
}
int l = 1, r = (n << 1) - 1, mid, res = 1;
while(l <= r) {
mid = (l + r) >> 1;
if(Solve(mid)) {
res = mid, l = mid + 1;
}
else {
r = mid - 1;
}
}
Write(res);
return 0;
}
E - Rotate 3x3
观察对一个
- 对于某一列的几个数,操作后必定还在同一列;
- 某一个数,其所在列的编号的奇偶性永远不变;
- 对于第
列从上到下 三个数,假设其被操作到了某一列,这一列从上到下要么是 ,要么是 。
然后根据上述操作判掉一些无解情况。
根据第三条性质,我们可以将从上到下形如
我们又可以得到:
与 奇偶性相同;- 考虑依次执行
,可以记录 ,可以观察到存在一种操作方式,使 均取反( ):
执行操作 | |||||
---|---|---|---|---|---|
- 考虑依次执行
,可以记录 ,可以观察到存在一种操作方式,使 均取反( ):
执行操作 | ||||
---|---|---|---|---|
- 综合以上两条,可以发现存在一种操作方式,使
均取反( )。
我们将
我们反过来考虑,即将目标序列转化为原序列,发现这恰好对应着排序。
因为一次操作对拆开后的序列相当于交换操作,所以对其操作的个数与逆序对数的奇偶性相同(这里我们是将满足
但是我们会发现一个问题,对某一序列的一次操作会使另一个序列的负数个数的奇偶性变化。所以只要两个序列都满足某一个序列逆序对数的奇偶性与另一个序列负数个数奇偶性相同即可。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Read() {
int sig = 1;
ll num = 0;
char c = getchar();
while(!isdigit(c)) { if(c == '-') sig = -1; c = getchar(); }
while(isdigit(c)) num = (num << 3) + (num << 1) + (c ^ 48), c = getchar();
return num * sig;
}
void Write(ll x) {
if(x < 0) putchar('-'), x = -x;
if(x >= 10) Write(x / 10);
putchar((x % 10) ^ 48);
}
const int N = 100005;
int n, a[3][N], p[N];
bool cnt[2], inv[2];
void Get(int x) {
if(a[0][x] + 1 == a[1][x] && a[1][x] + 1 == a[2][x] && a[2][x] % 3 == 0) {
p[x] = a[2][x] / 3;
if((p[x] & 1) ^ (x & 1)) printf("No"), exit(0);
}
else if(a[0][x] == a[1][x] + 1 && a[1][x] == a[2][x] + 1 && a[0][x] % 3 == 0) {
p[x] = a[0][x] / 3, cnt[x & 1] ^= 1;
if((p[x] & 1) ^ (x & 1)) printf("No"), exit(0);
}
else printf("No"), exit(0);
}
struct Tree_Array {
int n;
bool tree[N];
inline void Add(int x) { while(x) tree[x] ^= 1, x -= x & -x; }
inline bool Query(int x) {
bool res = 0;
while(x <= n) res ^= tree[x], x += x & -x;
return res;
}
}ta;
int main() {
int i, j; n = Read(), ta.n = n;
for(i = 0; i < 3; i++) for(j = 1; j <= n; j++) a[i][j] = Read();
for(i = 1; i <= n; i++) Get(i);
for(i = 1; i <= n; i += 2) inv[1] ^= ta.Query(p[i]), ta.Add(p[i]);
memset(ta.tree, 0, sizeof(ta.tree));
for(i = 2; i <= n; i += 2) inv[0] ^= ta.Query(p[i]), ta.Add(p[i]);
if((inv[1] ^ cnt[0]) || (inv[0] ^ cnt[1])) printf("No");
else printf("Yes");
return 0;
}
F - Blackout
考虑建图,若
考虑对整个图用三种颜色染色,首先:
- 弱联通块之间不能操作;
- 若一个弱连通块只染了两种颜色,显然不能对其操作。
排除掉上述情况之后,观察到如果一个弱联通分量能被成功三染色且至少有三种颜色,那么令
我们来证明这个结论:
如果一个弱联通分量能被成功三染色且至少有三种颜色,那么一定存在一个三元链,使得三个点
中,存在 两条边,显然这可以操作为一个三元环,即操作出 这条边。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】