[AtCoder Beginner Contest 476] Solution (ABCDEF)

这里是题目。

A – Appender

输入字符串$s$,如果$s$以字母 e 结尾,就在$s$末尾加上字母 r 后输出;否则在$s$末尾加上 er 后输出。

思路如题。

B – Wild Card

输入一个由小写字母构成的字符串$s$,与一个由小写字母和符号 * 构成的字符串$t$,判断能否通过更改$t$中每个 * 为某个小写字母以使得$t$等于$s$。

* 既然是随便给值,就不用考虑了,只需要考虑$t$中小写字母是否与$s$对应位置的小写字母一致即可。

C – Third Largest Number

输入一个长度大于等于3的序列$A$,对于每个$k = 3,\, 4,\, …,\, N$,输出当$A_1,\, A_2,\, …,\, A_k$降序排序时排在第三位的数。$N\leq 5\times 10^5$。

每次都在已经降序排序完毕的序列中插入一个数,使其仍满足降序排列,考虑使用大根堆,每次输出时,只需取出堆顶两个元素,此时堆顶即为第三大的数,当然还要把取出的两个元素再放回去。时间复杂度$O(N\log N)$。

D – Automat

有$N$个甜点和$M$个饮料,第$i$个甜点价格为$A_i$,第$j$个饮料价格为$B_j$。甜点可以用“1元钞票”和“$K$元钞票”购入,但饮料只能用“$K$元钞票”买,每种甜点或饮料最多买一个。找零时只会给“1元钞票”。求使用$X$张“1元钞票”和$Y$张“$K$元钞票”最多可以购买多少个商品(即甜点与饮料个数和最大值)。$N,\, M\leq 2\times 10^5$。

可以贪心。饮料要求更严格,就先考虑买多少个饮料,再看看买完饮料剩下的钱最多能买多少个甜点。即从$0$到$m$枚举买的饮料个数,计算剩余钱数能买的最大甜点数,每次更新饮料与甜点个数和最大值。时间复杂度$O(N\log N)$,含排序、二分等,详见代码。

signed main(){ // 说明开了long long
	cin >> n >> m >> k >> x >> y;
	for(int i = 1; i <= n; i++) cin >> a[i];
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; i++) sa[i] = sa[i - 1] + a[i];
	for(int i = 1; i <= m; i++) cin >> b[i];
	sort(b + 1, b + m + 1);
	for(int i = 1; i <= m; i++){
		sb[i] = sb[i - 1] + b[i];
		scb[i] = scb[i - 1] + ceil(b[i] * 1.0 / k); // 买1 ~ i号饮料需要几张K钱
	}
	for(int i = 0; i <= m; i++){ // 枚举饮品数
		if(scb[i] > y) break; // 此时K钱已经不够,直接break
		int ca = y - scb[i]; // 剩下ca张K钱
		int pm = k * scb[i] - sb[i]; // 买饮料给的零钱
		int p = upper_bound(sa + 1, sa + n + 1, ca * k + x + pm) - sa - 1; // 找最大甜点数
		ans = max(ans, p + i); // 更新个数和最大值
	}
	cout << ans << '\n';
	return 0;
}

E – Min-Max Swap

给出一个从$1$到$N$的排列$P$,进行$M$次操作,每次操作给出$l,\, r$,将$P$中下标位于$[l,\, r]$之间的最大元素与最小元素互换位置。输出进行完$M$次操作后的$P$。$N,\, M\leq 2\times 10^5$。

对于每次操作,先要找到区间最大最小值,接下来的“互换位置”这一操作我这样进行:将区间最大最小值分别存在$ma,\, mi$里,将$ma$所在位置元素更新为$mi$、$mi$所在位置元素更新为$ma$,再交换$ma$与$mi$的所在位置。这里有 query 和 update,考虑线段树。时间复杂度$O(M\log N)$。

int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> a[i]; ps[a[i]] = i; // ps[x]表示元素x所在位置
	}
	build(1, 1, n);
	for(int i = 1; i <= m; i++){
		int l, r;
		cin >> l >> r;
		int ma = getmax(1, l, r);
		int mi = getmin(1, l, r);
		update(1, ps[ma], mi); // 这里是在线段树上更新
		update(1, ps[mi], ma);
		a[ps[ma]] = mi; // 这里是在原序列中更新
		a[ps[mi]] = ma;
		swap(ps[ma], ps[mi]);
	}
	for(int i = 1; i <= n; i++) cout << a[i] << ' ';
	cout << '\n';
	return 0;
}

F – Chebyshev Cafe

描述题意好麻烦~~~因此省略了~~~($N\leq 1500,\, M\leq 2\times 10^6$)

我很少做出F题,即便AC了也不确定方法是否“正”,只是单纯观察出了一个做法。不知道有没有利用切比雪夫距离?

想到$f_{i,\, j}$是能够通过前值转移出来的,于是先$n^2$跑出$f_{1,\, 1}$,然后思考应该怎么转移。

因为是观察出来的,所以需要通过画图来描述。如上图,考虑由$f_{i,\, j-1}$推出$f_{i,\, j}$,发现黄色格子上的人要走的距离增加了$1$,粉色格子上的人要走的距离减少了$1$,而灰色格子上的人要走的距离不变。这样一来,就有:$$f_{i,\, j}=f_{i,\, j-1}+sum_{yellow}-sum_{pink}\quad (j>1).$$

其中$sum_{color}$表示颜色为 color 的格子上的总人数。不过,这个式子要求从左边的格子转移过来,那么如果是第 1 列的格子呢?

如上图,若格子位于第 1 列,则考虑从该格子上方的那个格子转移。类似地,蓝色格子上的人要走的距离增加了$1$,绿色格子上的人要走的距离减少了$1$,而灰色格子上的人要走的距离不变。这样一来,就有:$$f_{i,\, j}=f_{i-1,\, j}+sum_{blue}-sum_{green}\quad (j=1).$$

可这个$sum_{color}$看起来太抽象了,要具体地去考虑该怎么求。观察发现,每种颜色都是从某个固定的格子向着某个固定的方向“扩散”,实际上就是:黄色格子向左扩散、粉色格子向右扩散、蓝色格子向上扩散、绿色格子向下扩散。

这里我将$sum_{color}$“替换”为$ks_{d,\, i,\, j}$,表示格子$(i,\, j)$向$d$方向扩散到的格子上的总人数。其中$d=0,\, 1,\, 2,\, 3$分别对应向左、向右、向上、向下。那么,式子就可以变为:$$f_{i,\, j}=f_{i,\, j-1}+ks_{0,\, i,\, j-1}-ks_{1,\, i,\, j}\quad (j>1),$$$$f_{i,\, j}=f_{i-1,\, j}+ks_{2,\, i-1,\, j}-ks_{3,\, i,\, j}\quad (j=1).$$

接下来,问题又变为了如何求$ks$。先只考虑向左扩散,见下图:

不难发现,$ks_{0,\, i,\, j}$相对于$ks_{0,\, i,\, j-1}$来说,只是多了“向左上的一串”和“向左下的一串”。那么再考虑向其他方向扩散,其实是类似的,总的来说,需要求出每个格子对应的“向左上的一串”“向左下的一串”“向右上的一串”“向右下的一串”这 4 条串。可以设$ds_{e,\, i,\, j}$表示格子$(i,\, j)$“向$e$方向的一串”上的总人数,其中$e=0,\, 1,\, 2,\, 3$分别对应左上、右上、左下、右下。至于这个$ds$是怎么求的我就不说了,因为很容易(也因为我没精力了)。

所以,拿向左扩散为例,$ks$这么转移:$$ks_{0,\, i,\, j}=ks_{0,\, i,\, j-1}+ds_{0,\, i,\, j}+ds_{2,\, i,\, j}-c_{i,\, j}.$$

其中$c_{i,\, j}$表示格子$(i,\, j)$上的人数,这个一开始就可以求出来。这里显然要减去$c_{i,\, j}$,因为重复计算了。

终于终于~~~$ks$整完了,进而$f$也就整完了。不过,还要注意,最后输出的不是$f$中的所有元素直接异或起来的值,而是所有$f_{i,\, j}+(i-1)N+(j-1)$异或起来的结果。部分代码见下:

void init(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++) cin >> a[i];
	for(int i = 1; i <= n; i++) cin >> b[i];
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++) c[i][j] = (a[i] * b[j]) % m;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++) f[1][1] += max(i - 1, j - 1) * c[i][j];
}
int ds[4][N][N]; // 0-左上 1-右上 2-左下 3-右下
int ks[4][N][N]; // 0-左   1-右   2-上   3-下
void work1(){
	for(int i = 1; i <= n; i++) // 从上往下
		for(int j = 1; j <= n; j++)
			ds[0][i][j] = ds[0][i - 1][j - 1] + c[i][j], // 左上
			ds[1][i][j] = ds[1][i - 1][j + 1] + c[i][j]; // 右上
	for(int i = n; i >= 1; i--) // 从下往上
		for(int j = 1; j <= n; j++)
			ds[2][i][j] = ds[2][i + 1][j - 1] + c[i][j], // 左下
			ds[3][i][j] = ds[3][i + 1][j + 1] + c[i][j]; // 右下
	// 开始扩散
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			ks[0][i][j] = ks[0][i][j - 1] + ds[0][i][j] + ds[2][i][j] - c[i][j]; // 左
	for(int i = 1; i <= n; i++)
		for(int j = n; j >= 1; j--)
			ks[1][i][j] = ks[1][i][j + 1] + ds[1][i][j] + ds[3][i][j] - c[i][j]; // 右
	for(int j = 1; j <= n; j++)
		for(int i = 1; i <= n; i++)
			ks[2][i][j] = ks[2][i - 1][j] + ds[0][i][j] + ds[1][i][j] - c[i][j]; // 上
	for(int j = 1; j <= n; j++)
		for(int i = n; i >= 1; i--)
			ks[3][i][j] = ks[3][i + 1][j] + ds[2][i][j] + ds[3][i][j] - c[i][j]; // 下
}
void work2(){
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++){
			if(i == 1 && j == 1) continue; // 跳过(1, 1)
			if(j == 1)
				f[i][j] = f[i - 1][j] + ks[2][i - 1][j] - ks[3][i][j];
			else
				f[i][j] = f[i][j - 1] + ks[0][i][j - 1] - ks[1][i][j];
		}
}
void getans(){
	int ans = 0;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			ans ^= (f[i][j] + (i - 1) * n + j - 1);
	cout << ans << '\n';
}
signed main(){
	init();
	work1();
	work2();
	getans();
	return 0;
}

还没说时间复杂度呢,按量级说应该是$O(N^2)$。虽然这里有好多个$N^2$,不过问题不大,况且这道题时限给到了 3 秒,比其他题多了整整 1 秒。

G – Increasing Popcount

别看了,啥也没有(苦笑)。

最后

写题解可太累了,但很喜欢,可谓酣畅淋漓。

——— END ———

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇