Codeforces Global Round 20 A~F1题解

太困,实在撑不住,F2早上起来再补罢,抱歉了。


A. Log Chopping

题意:

给了一堆长度不一的木头,问能够剁几刀。

思路:

直接计算即可。

时间复杂度: O ( n ) O(n) On

int n;
signed main() {
	cf{
		sf(n);
		int res = 0;
		for (int i = 1, x; i <= n; i++)
			sf(x), res += x - 1;
 
		if (res & 1)
			puts("errorgorn");
		else
			puts("maomao90");
	}
	return 0;
}

B. I love AAAB

题意:

A B 、 A A B 、 A A A B … … AB、AAB、AAAB…… ABAABAAAB 按照任意顺序插入一个空字符串,能否得到所给字符串。

思路:

如果所给字符串能够被构造的话,一定会符合两种条件:

  • 最后一个字符为 B B B
  • 所有的前缀子串中,字符 A A A 的数量必定 ≥ \geq 字符 B B B 的数量。

时间复杂度: O ( n ) O(n) On

signed main() {
	cf{
		string res;
		cin >> res;
		bool st = true;
		int a = 0, b = 0;
		for (auto i : res)
			if (i == 'B') {
				b++;
				if (a < b) {
					st = false;
					break;
				}
			} else
				a++;

		if (res[res.size() - 1] == 'A')
			st = false;

		if (st)
			puts("YES");
		else
			puts("NO");
	}
	return 0;
}

C. Unequal Array

题意:

给定一个长度为 n n n 的数组 s s s,定义一种操作,将 s [ i ] , s [ i + 1 ] s[i],s[i+1] s[i]s[i+1] 的值改为任意一个值 x x x 1 ≤ i < n 1\leq i<n 1i<n

问最少进行多少次操作,使得 1 ≤ i < n 1\leq i<n 1i<n s [ i ] = = s [ i + 1 ] s[i]==s[i+1] s[i]==s[i+1] i i i 的个数 ≤ 1 \leq 1 1

思路:

这又是一道结论题。
找到相邻数相同的区间的最左和最右端点,我们想要做的就是将 [ l , r ] [l,r] [l,r] 这个区间中的相邻数相同的情况减为 1 1 1,那么操作次数就是 r − l − 2 r - l - 2 rl2
这里需要特判下原本就为零的情况与计算出的操作数为非正数的情况。

时间复杂度: O ( n ) O(n) On

int n;
int s[N];
signed main() {
	cf{
		sf(n);
		for (int i = 1; i <= n; i++)
			sf(s[i]);
 
		int tmp = 0, l = n + 1, r = 0;
		vector<int> ans;
		for (int i = 1; i < n; i++)
			if (s[i] == s[i + 1])
				l = min(l, i), r = max(r, i + 1);
 
		if (r - l <= 1)
			puts("0");
		else
			pfn(max(r - l - 2, 1ll));
	}
	return 0;
}

D. Cyclic Rotation

题意:

给定一个长度为 n n n 的数组 s s s,可以对其进行任意次数的以下操作:
选择两个下标 l 、 r , s [ l ] = = s [ r ] ( 1 ≤ l < r ≤ n ) l、r,s[l]==s[r](1\leq l<r\leq n) lrs[l]==s[r]1l<rn ,将区间内的数循环左移一步,即:
s [ l … … r ] = > s [ l + 1 , l + 1 , l + 3 … … r , l ] s[l……r] => s[l+1,l+1,l+3……r,l] s[lr]=>s[l+1,l+1,l+3r,l]
问是否可以通过任意次操作,将原数组转化为目标数组。

思路:

这道题感觉是模拟加一点点贪心。
具体代码讲解有些繁琐,这里就大致讲一下思路罢:

首先要确定一点,这两个数组的最后一个数必须相同,否则必然无法转化;
那么接下来就顺利成章的从后往前遍历,也就是模拟它循环左移的过程:
如果遍历到下标 i i i a [ i ] ≠ b [ i ] a[i]\neq b[i] a[i]=b[i] ,我们需要判断前面是否还有 b [ i ] b[i] b[i] ,如果没有自然可以直接 f a l s e false false,有的话,就需要以 i i i 为右端点进行一次操作。
但是左端点呢?这里不能早早地定下左端点,需要贪心和下标最小的值为 b [ i ] b[i] b[i] 的位置 j j j 进行交换。如果该数组可以转化成功的话,如果 b [ j ] = = a [ j ] b[j]==a[j] b[j]==a[j] 自然是不用变换的,但是 b [ j ] ≠ a [ j ] b[j]\neq a[j] b[j]=a[j] 的话,这里的 b [ j ] b[j] b[j] 必定是作为一个左端点与后面的点共同进行一次操作。

所以大致思路就是模拟这一过程,如果遍历到不相同的点时,它右边的点能够匹配、前面的相同值的下标足够进行匹配的话,那就直接进行一次操作;否则就判断它是否能够作为操作的左端点,能就重新判断下一个点,否则就直接 f a l s e false false

Ps.代码中我用 j j j 来表示遍历到该点时正在多少次操作之中。

人菜码多别骂了,佬们应该有更简便的解法

时间复杂度: O ( n ∗ log ⁡ n ) O(n*\log{n}) Onlogn

int n;
int a[N], b[N];
signed main() {
	cf{
		sf(n);
		map<int, vector<int> > q;
		map<int, int> d;
		for (int i = 1; i <= n; i++)
			sf(a[i]), q[a[i]].push_back(i);
		for (int i = 1; i <= n; i++)
			sf(b[i]), d[b[i]] = 0;

		if (a[n] != b[n]) {
			puts("NO");
			continue;
		}

		bool st = true;
		int j = 0;
		for (int i = n - 1; i >= 1 - j; i--)
			if (a[i + j] != b[i]) {
				if (b[i] != b[i + 1] || a[i + j + 1] != b[i + 1]
				        || (int)(lower_bound(all(q[b[i]]), i + j + 1) - q[b[i]].begin()) <= d[b[i]]) {

					if (d[a[i + j]] > 0) {
						d[a[i + j]]--;
						j--;
						i++;
						continue;
					}
					st = false;
					break;
				} else {
					j++;
					d[b[i]]++;
				}
			}
		if (st)
			puts("YES");
		else
			puts("NO");
	}
	return 0;
}

E. notepad.exe

题意:

n n n 个长度未知的字符串数组,你可以进行最多 n + 30 n+30 n+30 次询问。

对于每次询问,你可以定一个宽度 w w w,如果这个宽度比最长的字符串还要短,则会返回 0 0 0 ;否则,在两个相邻字符串之间至少间隔一个空格、每行的长度不超过所给的宽度的情况下,所能够得到的最小的长度 h h h

求该字符串数组能够得到的 h ∗ w ≠ 0 h*w\neq 0 hw=0 的情况下的最小值。

思路:

这道题是赛后补的,交互题实在是有些不擅长。

看到 n + 30 n+30 n+30 次的询问次数,有经验的佬们能看出 30 30 30 代表需要二分,那么我们想下这种情况需要二分什么?
仔细想想,我们能够二分出来的也就只有在 h = = 1 h==1 h==1 的情况下, w w w 的最小值,也就是字符串数组写为一行的宽度。

知道了整个“大”字符串的长度,那么接下就只需要遍历 h h h ,并求出对应的 h ∗ w h*w hw,最后取一个最小值即可。

时间复杂度: O ( n ) O(n) On

int ask(int u) {
	cout << "? " << u << '\n';
	fflush(stdout);
	int tmp;
	sf(tmp);
	return tmp;
}
signed main() {
	int n;
	sf(n);
	int l = 1, r = 2010 * 2010;
	while (l < r) {
		int mid = l + r >> 1;
		if (ask(mid) == 1)
			r = mid;
		else
			l = mid + 1;
	}

	int res = l;
	for (int i = 1; i <= n; i++) {
		int x = ask(l / i);
		if (x ) {
			res = min(res, l / i * x);
		}
	}
	cout << "! " << res << "\n";
	return 0;
}

F1. Array Shuffling

题意:

给出一个长度为 n n n 的数组 a a a,并定义一种操作:
选择两个下标 i , j ( 1 ≤ i , j ≤ n ) i,j(1\leq i,j\leq n) ij1i,jn,交换两个下标对应的值。

问怎样将其重新排列,使得得到的数组 b b b 需要最多次操作才能将其还原为 a a a 数组,这里还原的操作为最优解。

思路:

就……很巧,这道题的逆题之前记得佬们聊起过,所以这道题算半个结论题吧,结论与证明 在此。

我们已经知道,想要让还原数组的最少操作次数最多,就要使操作中的环的个数最少;又因为,一个交换操作对应的环中不能有相同的值。
那么做法就很明确了,每次都用最多的不同的数取构造环,然后将环上的值循环错位,直到剩下的数的种类只有一种为止。

时间复杂度: O ( n ) O(n) On

int n;
int s[N];
signed main() {
	cf{
		sf(n);
		map<int, queue<int> > d;
		for (int i = 1; i <= n; i++)
			sf(s[i]), d[s[i]].push(i);

		vector<PII> tmp1;
		for (auto i : d)
			tmp1.push_back({i.y.size(), i.x});
		queue<int> q;
		for (auto i : tmp1)
			q.push(i.y);

		while (q.size() > 1) {
			int len = q.size();
			int t = q.front(), x;
			q.pop();
			if (d[t].size() > 1)
				q.push(t);
			int tmp = d[t].front();
			d[t].pop();
			x = t;
			len--;
			while (len--) {
				int j = q.front();
				q.pop();
				if (d[j].size() > 1)
					q.push(j);
				int v = d[j].front();
				d[j].pop();
				s[v] = t;
				t = j;
			}
			s[tmp] = t;
		}
		for (int i = 1; i <= n; i++)
			pft(s[i]);
		puts("");
	}
	return 0;
}

F2. Checker for Array Shuffling

题意:

思路:

时间复杂度: O ( ) O() O


  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值