Codeforces Round #807 (Div. 2)

这场上了80分,刚好上蓝嘿嘿。

传送门https://codeforces.com/contest/1705

A. Mark the Photographer

题意:2n个人排成两排,每排n个人。要求后面的人要比前面的人高至少x

问是否可以。

:直接升序排序,第i矮的站在第i+n矮的前面是最优的。

#include<bits/stdc++.h>
#define int long long 
const int mo = 998244353;
const int N = 1e6;

signed main() {
	std::ios::sync_with_stdio(false);
	int t; std::cin >> t;
	while (t--) {
		int n, x; std::cin >> n >> x;
		int h[210] = { 0 };
		for (int i = 1; i <= 2 * n; i++)std::cin >> h[i];
		std::sort(h + 1, h + 1 + 2 * n);
		bool f = 1;
		for (int i = 1; i <= n; i++) {
			if (h[i + n] - h[i] < x)f = 0;
		}
		if (f)std::cout << "YES" << std::endl;
		else std::cout << "NO" << std::endl;
	}
}

B. Mark the Dust Sweeper

题意:有一个数组a,有一种操作:

每次选择一个i,j。要求i<j并且\(a_i\)\(a_{j-1}\) 都大于0。

然后\(a_i\)-1,\(a_j\)+1。

问最少多少步使得\(a_1\)\(a_{n-1}\)全为0

解法

如果一段正整数连续,那么最前面的数字可以直接移动到最后面。

如果中间被0断开,可以花最小的代价,用1在这些0上铺出一条路。

比如 :0 3 0 0 0 0 1 3

可以先铺成:0 1 1 1 0 0 1 3

然后这段连续的1无法再铺了,那么就让它蠕动前进。也就是第一个1铺到最后一个1后面:0 0 1 1 1 0 1 3

然后就变成了: 0 0 0 1 1 1 1 3

此时就连通了。答案就是之前铺路的损耗加上现在每个数字直接到达\(a_n\)的代价。

#include<bits/stdc++.h>
#define int long long 
const int mo = 998244353;
const int N = 1e6;
int a[N];
signed main() {
	std::ios::sync_with_stdio(false);
	int t; std::cin >> t;
	while (t--) {
		int n; std::cin >> n;
		for (int i = 1; i <= n; i++)std::cin >> a[i];
		int ans = 0;
		int p = 1;
		for (int i = 1; i < n; i++) {
			while (!a[p] && p <= n)p++;
			if (p > i)i = p;
			if (i >= n)break;
			if (a[i] == 0) {
				a[p]--;
				a[i] = 1;
				ans++;
			}
		}
		for (int i = 1; i < n; i++)ans += a[i];
		std::cout << ans << std::endl;
	}
}

C. Mark and His Unfinished Essay

题意:给一个字符串,有c次操作,q次询问。每次操作都把字符串的[l,r]区间复制粘贴到字符串末尾,字符串更新。每次询问输出字符串第k位的字符是什么。

思路:显然暴力模拟,空间是开不下的,因为都肥到1e18了。考虑到操作次数不多,只有40,可以记录每次操作,然后对每次询问都遍历操作进行模拟。(一开始没发现只有40次操作,撒呼呼写了二分,其实不二分也可以的)

比如询问k,我们可以去寻找k在哪次操作之后的区间。找到这个操作。那么相当于就是问k-这次操作之前的长度+这次操作的起点-1位置的字符。

然后就循环向回跑,直到跑到第一次操作之前。此时就可以直接得到答案了

#include<bits/stdc++.h>
#define int long long 
const int mo = 998244353;
const int N = 1e6;
int a[N];
signed main() {
	std::ios::sync_with_stdio(false);
	int t; std::cin >> t;
	while (t--) {
		int n; std::cin >> n;
		int c, q; std::cin >> c >> q;
		std::string s; std::cin >> s;
		std::vector<std::pair<int, int>>ve;
		ve.push_back({ 0,0 });
		ve.push_back({ 1,n });
		std::vector<int>h;
		h.push_back(0);
		h.push_back(n);
		int lasth = n;
		while (c--) {
			int a, b; std::cin >> a >> b;
			ve.push_back({ a,b });
			lasth += b - a + 1;
			h.push_back(lasth);
		}
		while (q--) {
			int k; std::cin >> k;
			int p = std::lower_bound(h.begin(), h.end(), k) - h.begin();
			int l = k - h[p - 1] + ve[p].first -1;
			while (p != 1) {
				//std::cout << "p="<<p << "\n";
				p = std::lower_bound(h.begin(), h.end(), l) - h.begin();
				//std::cout << "l=" <<l<< std::endl; std::cout << "p=" << p << "\n";
				l = l - h[p - 1] + ve[p].first -1;
			}
			std::cout<< s[l-1] << "\n";
		}
	}
}

D. Mark and Lightbulbs

题意:有2个字符串,要从第一个字符串s变到第二个字符串t。求最小操作次数,如果不可能则输出-1。

操作:选择一个位置i,要求s[i-1]与s[i+1]不同,i属于[2,n-1] (从1开始储存)。翻转s[i]。

也就是说:011可以变成 001 ,010不可操作。

解法

不难发现 对一个连续的1区间: 00001110

可以变成:00011110,也可以变成 00000110

也就是说,每一个连续的1区间可以伸缩蠕动,而且不管怎么蠕动,区间数不会变少也不会变多。

于是就好解决了:把两个字符串都拆成连续的1区间。

如果区间数不同则输出-1。

如果相同,则遍历所有连续1的区间,t的第一个目标区间一定是由s的第一个区间蠕动得到的,操作次数就是区间头尾对应差的绝对值之和了。

注意,由于1和n的位置无法翻转,所以无法蠕动到,如果不同一定是不可能,输出-1

#include<bits/stdc++.h>
#define int long long 
const int mo = 998244353;
const int N = 1e6;
int a[N];
std::string s, t;
std::vector<std::pair<int, int>>vs,vt;

signed main() {
	std::ios::sync_with_stdio(false);
	int q; std::cin >> q;
	while (q--) {
		int n; std::cin >> n;
		vs.clear(); vt.clear();
		std::cin >> s >> t;
		if (s == t) {
			std::cout << 0 << "\n";
			continue;
		}
		if (s[0] != t[0] || s[n - 1] != t[n - 1]) {
			std::cout << -1 << "\n";
			continue;
		}
		
		int l = 0;
		for (int i = 0; i < n; i++) {
			if (s[i] == '1' && (!i || s[i - 1] == '0'))l = i;
			if (s[i] == '1' && (i == n - 1 || s[i + 1] == '0')) {
				vs.push_back({ l,i });
			}
		}
		l = 0;
		for (int i = 0; i < n; i++) {
			if (t[i] == '1' && (!i || t[i - 1] == '0'))l = i;
			if (t[i] == '1' && (i == n - 1 || t[i + 1] == '0')) {
				vt.push_back({ l,i });
			}
		}
		
		if (vt.size() != vs.size()) {
			std::cout << -1 << "\n";
			continue;
		}
		int ans = 0;
		//bool f = 1;
		//int last = -2;
		for (int i = 0; i < vs.size(); i++) {
			//if (last + 1 >= vs[i].first)f = 0;
			ans += std::abs(vs[i].first - vt[i].first) + std::abs(vs[i].second - vt[i].second);
			//last = vt[i].second;
		}
		//if (!f) std::cout << -1 << "\n";
		//else 
		std::cout << ans << "\n";
		
	}
}

E. Mark and Professor Koro

看成2进制进位。线段树操作。

传送门:https://codeforces.com/contest/1705/problem/E

wa3

#include<bits/stdc++.h>
#define int long long 
const int N = 1e6;
int a[N];
int mp[N];
signed main() {
	std::ios::sync_with_stdio(false);
	int n, q; std::cin >> n >> q;
	for (int i = 1; i <= n; i++) {
		std::cin >> a[i];
		mp[a[i]]++;
	}
	int ans = 0;
	for (int i = 1; i <= 3e5; i++) {
		mp[i + 1] += mp[i] / 2;
		if (mp[i + 1])ans = i + 1;
	}
	while (q--) {
		//ans = 0;
		int k, l;
		std::cin >> k >> l;
		mp[a[k]]--;
		int p = a[k];
		while (mp[p] % 2 == 1) {
			if (mp[p + 1] == 0)break;
			mp[p + 1]--;
			if (mp[p + 1] > 0)ans = p + 1;
			p++;
		}
		
		mp[l]++;
		p = l;
		while (mp[p] % 2 == 0) {
			mp[p + 1]++;
			ans = std::max(p + 1, ans);
			p++;
		}
		
		a[k] = l;
		if (!mp[ans]) {
			for (int i = ans - 1; i >= 1; i--)if (mp[i]) {
				ans = i;
				break;
			}
		}
		std::cout << ans << "\n";
	}
}

posted on 2022-07-18 11:18  wtn135687  阅读(81)  评论(0)    收藏  举报