Codeforces Round (2021-01-14)

Published 2021-01-13 16:00 1340 words 7 min read Handwritten Every word here was typed by me.

Zhiheng Wang avatar

Zhiheng Wang

技术文章、个人随笔、项目与作品体验记录

This post is not yet available in English. Showing the original.
Educational Codeforces Round 102 (Rated for Div. 2) A. Replacing Elements 题目链接 给一个正整数数组,每次可以选三个不同位置,把其中一个数换成另外两个数的和。问能不能让所有数都不超过 $d$。 如果已经都不超过 $d$,当然直接结束。否则就看最小的两个数加起来能不能不超过 $d$。...

Educational Codeforces Round 102 (Rated for Div. 2)

A. Replacing Elements

题目链接

题意

给一个正整数数组,每次可以选三个不同位置,把其中一个数换成另外两个数的和。问能不能让所有数都不超过 dd。

思路

如果已经都不超过 dd,当然直接结束。否则就看最小的两个数加起来能不能不超过 dd。

能的话,保留这两个数,拿它们的和替换所有超标的数。连最小的两个数都加大了,第一次操作就造不出一个不超过 dd 的数,后面也帮不上忙。排序后判断,时间 O(nlog⁡n)O(n\log n)。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

const int maxn = 1e3 + 17;
int T, n, a[maxn], d;

int main() {
	cin >> T;
	while(T--) {
		cin >> n >> d;
		int flg = 0;
		for(int i = 1; i <= n; i++) {
			cin >> a[i];
			if(a[i] > d) flg = 1;
		}
		if(flg) {
			sort(a + 1, a + 1 + n);
			if(a[1] + a[2] <= d) flg = 0;
		}
		puts(flg ? "NO" : "YES");
	}
	return 0;
}

B. String LCM

题目链接

题意

给两个字符串,找最短的非空字符串,使它既能由第一个串重复得到,也能由第二个串重复得到。如果不存在就输出 -1。

思路

分别找出两个串最短的循环节。先枚举循环节长度,只有能整除串长的长度才考虑,再逐段比较。

循环节不同就拼不到一起。相同的话,假设两边各重复了 x,yx,y 次,再把这个循环节重复 lcm⁡(x,y)\operatorname{lcm}(x,y) 次就行。这里串很短,直接枚举和比较就够了。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

int T;

inline int gcd(int x, int y) {
	return y ? gcd(y, x % y) : x;
}

int main() {
	cin >> T;
	while(T--) {
		string s1, s2;
		cin >> s1; cin >> s2;
		int len1 = s1.length(), len2 = s2.length();
		int l1 = 0, l2 = 0;
		for(l1 = 1; l1 <= len1; l1++) {
			if(len1 % l1) continue;
			int flg = 1;
			for(int i = l1; i < len1;) {
				for(int j = 0; j < l1; j++, i++) {
					if(s1[i] != s1[j]) flg = 0;
				}
			}
			if(flg) break;
		}
		for(l2 = 1; l2 <= len2; l2++) {
			if(len2 % l2) continue;
			int flg = 1;
			for(int i = l2; i < len2;) {
				for(int j = 0; j < l2; j++, i++) {
					if(s2[i] != s2[j]) flg = 0;
				}
			}
			if(flg) break;
		}
		if(l1 == l2) {
			int flg = 1;
			for(int i = 0; i < l1; i++) {
				if(s1[i] != s2[i]) flg = 0;
			}
			if(flg) {
				int x = len1 / l1, y = len2 / l2;
				int rep = x * y / gcd(x, y);
				for(int i = 1; i <= rep; i++) {
					for(int j = 0; j < l1; j++) printf("%c", s1[j]);
				}
				printf("\n");
			}
			else {
				puts("-1");
			}

		}
		else puts("-1");
	}
	return 0;
}

C. No More Inversions

题目链接

题意

序列先从 1 升到 kk,再降到 2k−n2k-n,总长为 nn。要找一个 1…k1\ldots k 的排列 pp,用 p[ai]p[a_i] 替换每一项,要求逆序对不增加,并让新序列字典序最大。

思路

设 r=2k−nr=2k-n。小于 rr 的数只出现一次,后面的数除了 kk 都出现两次。

对后面这组数,两个不同的数不管谁大,贡献的逆序对数都一样;和中间只出现一次的 kk 比,也始终贡献一次。所以这组可以随便换顺序,直接倒过来让前面的数尽量大。

前面只出现一次的部分原本没有逆序对,动它只会增加,保留升序就行。最后输出 1...r-1,再输出 k...r,时间 O(k)O(k)。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

int T, n, k;

int main() {
	cin >> T;
	while(T--) {
		cin >> n >> k;
		for(int i = 1; i < 2 * k - n; i++) printf("%d%c", i, " \n"[i == k]);
		int j = k;
		for(int i = 2 * k - n; i <= k; i++, j--) printf("%d%c", j, " \n"[i == k]);
	}
	return 0;
}

D. Program

题目链接

题意

变量从 0 开始,程序里的 +、- 分别让它加 1、减 1。每个询问独立删掉一段指令,问执行剩下的程序时一共出现过多少种值,包括初始的 0。

思路

每次只走 1,最小值和最大值之间的整数都会经过,所以只要求出这两个端点。

先求前缀和,用 ST 表查区间最大最小值。删掉 [l,r][l,r] 后,前半段不变,后半段整体减去 s[r] - s[l-1],把两边的极值和 0 合起来,答案就是 mx - mn + 1。

建表花 O(nlog⁡n)O(n\log n)。这里查表前用循环找对数,每次询问是 O(log⁡n)O(\log n)。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

const int maxn = 2e5 + 17;;
int T, n, m, a[maxn], s[maxn], dmax[maxn][20], dmin[maxn][20];
void initMax(int n,int d[])
{
    for(int i=1;i<=n;i++)dmax[i][0]=d[i];
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
}
int getMax(int L,int R)
{
    int k=0;
    while((1<<(k+1))<=R-L+1)k++;
    return max( dmax[L][k],dmax[R-(1<<k)+1][k] );
}
void initMin(int n,int d[])
{
    for(int i=1;i<=n;i++)dmin[i][0]=d[i];
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dmin[i][j]=min( dmin[i][j-1],dmin[i+(1<<(j-1))][j-1] );
}
int getMin(int L,int R)
{
    int k=0;
    while((1<<(k+1))<=R-L+1)k++;
    return min( dmin[L][k],dmin[R-(1<<k)+1][k] );
}

int main() {
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d", &n, &m);
		string str;
		cin >> str;
		for(int i = 1; i <= n; i++) a[i] = (str[i - 1] == '+' ? 1 : -1);
		for(int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
		initMin(n, s);
		initMax(n, s);
		for(int i = 1; i <= m; i++) {
			int l, r;
			scanf("%d%d", &l, &r);
			int tmp = s[r] - s[l - 1];
			int mx = 0, mn = 0;
			if(l > 1) {
				mx = max(mx, getMax(1, l - 1));
				mn = min(mn, getMin(1, l - 1));
			}
			if(r < n) {
				mx = max(mx, getMax(r + 1, n) - tmp);
				mn = min(mn, getMin(r + 1, n) - tmp);
			}
			printf("%d\n", mx - mn + 1);
		}
	}
	return 0;
}

Written by Handwritten

Random Posts

© 2026 Zhiheng Wang @traceonsnow
Powered by theme astro-koharu · Inspired by Shoka
Loading...