AGC006 题解

A - Prefix and Suffix

字符串长度最小等价于两个字符串拼接后的“重叠部分”最长,枚举 s 的每一个后缀 s,再枚举 t 的长度为 |s| 的前缀 t,若 s=t,则 s 可作为重叠部分,找到满足条件的最长的 s,t,答案即为 2n|s|

#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

先考虑将 x 放在第 n 个位置(即正中间),我们尝试将 x 从中间“传送”上去。
一个直观的想法是在其左右分别放置一个小于 x 的数和一个大于 x 的数。
我们会惊喜地发现,只要满足上述条件就一定能把 x “传送”上去!

证明:考虑 x 左上方的格子,设 x 左边的格子里的数为 yy 左边的格子里的数为 z,分类讨论:

  • z<y<x,则中位数 y<x
  • y<z<x,则中位数 z<x
  • y<x<z,则中位数 x=x

可以发现无论怎样,第 n1 列的数一定 x,而同理可得第 n+1 列的数一定 x,则一定保证 x 能被传送上去。

所以任选 <x>x 的两个数即可(代码中选用了 x1x+1 两个数),注意特判 x=1,2n1 的情况。

#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

考虑每一次跳跃,设当前第 ai1,ai,ai+1 只兔子的坐标期望值为 x,y,z,则 ai 做一次跳跃可能跳到 2xy2zy 的位置,坐标期望值即为 x+zy
因此每一次操作等价为,给定 ai,令 xaixai1+xai+1xai
考虑差分,依旧设当前第 ai1,ai,ai+1 只兔子的坐标期望值为 x,y,z,跳跃后第 ai1,ai,ai+1 只兔子的坐标期望值为 x,y,z,则 yx=x+zyx=zyzy=zxz+y=yx,即每一次操作等价于交换 xi 的差分数组上的两个数。
对于一轮操作,我们维护操作后每个位置上对应原来的哪个值,倍增即可得到 K 轮操作后的结果。

#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

直接优化看起来没什么前途,注意到对于任意的集合 S|S|mod2=1),将数以 x 为界划分成 S1S2,使得 yS1yxyS2y>x,我们发现:若 S1>S2,则中位树一定小于等于 x,否则一定大于 x
考虑二分答案 x,然后我们将排列中小于等于 x 的数染成黑色,将大于 x 的数染成白色,就可以把一个排列转化为一个 01 串。
image
考虑上图在第一层有一组相邻的黑格和一组相邻的白格,可以直观的看出,两个黑格与两个白格一直斜着“传送”上去,在中间某一层“相遇”了,然后黑白格就有了一条分界线。因为黑格距离中间近,所以最上面的格子是黑色。
所以说我们直接找离中间最近的相邻两个格子就行了,显然的不可能有离中心一样近的两个格子。
还有一种情况,就是不存在任何一个离中心一样近的格子,模一下就会发现与最外面的格子颜色相同,于是就可以愉快的二分了。
image

#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

观察对一个 3×3 正方形的一次 180 旋转操作,发现:

  • 对于某一列的几个数,操作后必定还在同一列;
  • 某一个数,其所在列的编号的奇偶性永远不变;
  • 对于第 i 列从上到下 a1,i,a2,i,a3,i 三个数,假设其被操作到了某一列,这一列从上到下要么是 a1,i,a2,i,a3,i,要么是 a3,i,a2,i,a1,i

然后根据上述操作判掉一些无解情况。
根据第三条性质,我们可以将从上到下形如 3x2,3x1,3x 的一列编号为 x,形如 3x,3x1,3x2 的一列编号为 x,令第 i 列的编号为 pi,那么一次操作就相当于交换 pi,pi+2(记这次操作为 f(i+1)),再将 pi,pi+1,pi+2 同时取反。
我们又可以得到:

  • |pi|i 奇偶性相同;
  • 考虑依次执行 f(i),f(i+2),f(i),f(i+2),f(i),f(i+2),可以记录 pi1,pi,pi+1,pi+2,pi+3,可以观察到存在一种操作方式,使 pi,pi+2 均取反(1<i<n2):
执行操作 pi1 pi pi+1 pi+2 pi+3
f(i) pi+1 pi pi1 pi+2 pi+3
f(i+2) pi+1 pi pi+3 pi+2 pi1
f(i) pi+3 pi pi+1 pi+2 pi1
f(i+2) pi+3 pi pi1 pi+2 pi+1
f(i) pi1 pi pi+3 pi+2 pi+1
f(i+2) pi1 pi pi+1 pi+2 pi+3
  • 考虑依次执行 f(i),f(i+1),f(i),f(i+1),可以记录 pi1,pi,pi+1,pi+2,可以观察到存在一种操作方式,使 pi1,pi,pi+1,pi+2 均取反(1<i<n1):
执行操作 pi1 pi pi+1 pi+2
f(i) pi+1 pi pi1 pi+2
f(i+1) pi+1 pi+2 pi1 pi
f(i) pi1 pi+2 pi+1 pi
f(i+1) pi1 pi pi+1 pi+2
  • 综合以上两条,可以发现存在一种操作方式,使 pi,pi+2 均取反(1in2)。

我们将 pi 交错拆成两个序列,再考虑上述的性质相当于对于每个序列,只要其序列中的负数个数是 2 的倍数,那么我们就可以将其变成没有负数的序列。
我们反过来考虑,即将目标序列转化为原序列,发现这恰好对应着排序。
因为一次操作对拆开后的序列相当于交换操作,所以对其操作的个数与逆序对数的奇偶性相同(这里我们是将满足 x<y|px|>|py|(x,y) 当做一个逆序对)。
但是我们会发现一个问题,对某一序列的一次操作会使另一个序列的负数个数的奇偶性变化。所以只要两个序列都满足某一个序列逆序对数的奇偶性与另一个序列负数个数奇偶性相同即可。

#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

考虑建图,若 (x,y) 所在的格子是黑色的,那么直接添加一条有向边 (x,y)
考虑对整个图用三种颜色染色,首先:

  • 弱联通块之间不能操作;
  • 若一个弱连通块只染了两种颜色,显然不能对其操作。

排除掉上述情况之后,观察到如果一个弱联通分量能被成功三染色且至少有三种颜色,那么令 Sa 表示所有 a 颜色的点的集合,那么最多只能操作至形如 SaSb,SbSc,ScSa 三类边,否则没有限制。
我们来证明这个结论:

如果一个弱联通分量能被成功三染色且至少有三种颜色,那么一定存在一个三元链,使得三个点 u,v,w 中,存在 (u,v),(v,w) 两条边,显然这可以操作为一个三元环,即操作出 (v,w) 这条边。

posted @   Include_Z_F_R_qwq  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示