Codeforces Round 913 (Div. 3)(A~G)

A. Rook

题意:在一个国际象棋棋盘中,横坐标为a-h,纵坐标为1-8,字母在前,数字在后,输入一个棋子的位置,输出该棋子所在的行与列中非棋子本身位置的所有位置。

分析:模拟。

代码:

#include <iostream> 
#include <algorithm>
using namespace std;
typedef long long ll; 
const int N = 2e5 + 10;

int t, n, m;
ll s[N], f[N];
ll ans, k;
string str;

void sol() {
	cin >> str;
	for (int i = 0; i < 8; i++) {
		char ch = 'a' + i;
		if (ch == str[0]) continue;
		cout << ch << str[1] << endl;
	}
	for (int i = 1; i <= 8; i++) {
		if (i == str[1] - '0') continue;
		cout << str[0] << i << endl;
	}
}

int main()
{
	t = 1;
	cin >> t;
	while (t--)
		sol();
    return 0;
}

B - YetnotherrokenKeoard

题意:给出一串字符串,按顺序输入,但计算机遭到篡改,输入B会删除之前输出的大写字母,输入b会删除之前输出的小写字母,如果没有则不执行,询问最终输出结果
分析:栈的调用,大写字母和小写字母分别调用一个栈,标记删除的位置进行假删除,输出的时候跳过删除的位置即可。

代码:

#include <iostream> 
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
typedef long long ll; 
const int N = 2e5 + 10;

int t, n, m;
ll s[N], f[N];
ll ans, k;
string str;
stack<int> sta, stb;
map<int, int> ma;

void sol() {
	cin >> str;
	for (int i = 0; i < str.size(); i++) {
		if (str[i] == 'b') {
			ma[i] = 1;
			if (!sta.empty()) {
				ma[sta.top()] = 1;
				sta.pop();
			}
		}
		else if (str[i] == 'B') {
			ma[i] = 1;
			if (!stb.empty()) {
				ma[stb.top()] = 1;
				stb.pop();
			}
		}
		else if (str[i] >= 'a' && str[i] <= 'z') {
			sta.push(i);
		}
		else if (str[i] >= 'A' && str[i] <= 'Z') {
			stb.push(i);
		}
	}
	for (int i = 0; i < str.size(); i++) {
		if (ma[i]) continue;
		cout << str[i];
	}cout << endl;
	ma.clear();
	while (!sta.empty()) sta.pop();
	while (!stb.empty()) stb.pop();
}

int main()
{
	t = 1;
	cin >> t;
	while (t--)
		sol();
    return 0;
}

C - Removal of Unattractive Pairs

题意:给出n,长度为n的字符串合法范围内前后字母不同可以进行消除,问最终最少剩下几个字母(删除顺序不同答案不同,比如cabcc->ccc, cabcc->bcc->c,前者是3,后者是1,显然后者更优)。

分析:最终一定会变为全是某个字母的情况,观察例子发现,该字母一定是字符串中字母最多的那个,分析每次跟字母最多的进行匹配一定更优,假如最多的字母数量为x,如果x>n-x,那么最终答案一定为2*x-n,如果不是,那么一定会两两匹配,只需要判断n的奇偶即可。

代码:

#include <iostream> 
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
typedef long long ll; 
const int N = 2e5 + 10;

int t, n, m;
int s[N], f[N];
ll ans, k;
string str;
stack<int> sta, stb;
map<int, int> ma;

void sol() {
	cin >> n;
	cin >> str;
	for (int i = 0; i < str.size(); i++) {
		s[str[i] - 'a']++;
	}
	int maxn = 0;
	for (int i = 0; i < 26; i++) {
		maxn = max(maxn, s[i]);
		s[i] = 0;
	}
	if (maxn > str.size() - maxn) {
		cout << maxn - (str.size() - maxn) << endl;
	} else {
		cout << (str.size() & 1) << endl;
	}
}

int main()
{
	t = 1;
	cin >> t;
	while (t--)
		sol();
    return 0;
}

D - Jumping Through Segments

题意:给出n,以下n组,每组给出l, r,最初为于0的位置,每次可以走不超过k的位置,比如在k可以走到[0, 2*k]这个区间范围内任意地方,要求走n步,走第i步必须走到[l[i],r[i]]范围内,求k最小值。

分析:显然二分答案,问题是如何判断k的合法性,如果最初是在[a,b]的范围,那么一步走不超过k的范围,走一步后,范围必定在[a-k,b+k],如果跟当前要求的范围没有交集,意味着该k过小,不合法,如果有交集,那么因为必须走到要求范围内,所以会取共集,[max(l, a-k), min(r, b+k)]。一路走下去,时间复杂度(O)nlogn。为方便使用,二元组用了pair<int, int>。

代码:

#include <iostream> 
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
typedef long long ll; 
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 1e9 + 7;

int t, n, m;
int s[N], f[N];
ll ans, k;
PII p[N];
string str;
stack<int> sta, stb;
map<int, int> ma;

bool judge(int mid) {
	PII q = {0, 0};
	for (int i = 1; i <= n; i++) {
		q.first -= mid;
		q.second += mid;
		if (q.first > p[i].second) 
			return false;
		if (q.second < p[i].first)
			return false;
		q.first = max(q.first, p[i].first);
		q.second = min(q.second, p[i].second);
	}
	return true;
}

void sol() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int l, r;
		cin >> l >> r;
		p[i] = {l, r};
	}
	int l = 0, r = mod;
	while (l < r) {
		int mid = l + r >> 1;
		if (judge(mid))
			r = mid;
		else 
			l = mid + 1;
	}
	cout << l << endl;
}

int main()
{
	t = 1;
	cin >> t;
	while (t--)
		sol();
    return 0;
}

E - Good Triples

题意:众和数,即一个自然数的各位数字之和,例如345的众和数就是3+4+5=12,以下用digusm(n)表示n的众和数,条件1:a+b+c=n,条件2:digsum(a)+digsum(b)+digsum(c)=digsum(n),给出n,能找到多少个满足以上两个条件的答案。n≤1e7

分析:t组,n范围这么大,还没说所有的n不超过1e7,均下来每个只有不超过1e4的次数查答案,根号n级别,过题人数上千,考虑找规律,先暴力模拟,找规律。在模拟后发现每一位其实是单独计算的,记f(n)为答案,0-9的答案是直接算出来的,其余都是临时算的,是每一位答案的乘积,比如f(345)=f(3) * f(4) * f(5),找到规律后,查找一个的时间复杂度极低,可以通过。至于为什么可以每位单独考虑,在知道答案后分析,可以发现,分别考虑就是每位不进到下一位的情况,如果有进位的情况意味着在本位算数的之后会多10,而众和数那边这个10对不回去,一个数只能有不超过10的大小,且对应的数进位就连锁,不进位该数对于另外的10将毫无贡献,所以每一位只能单独计算,互不影响。

代码:

#include <iostream> 
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
typedef long long ll; 
typedef pair<int, int> PII;
const int N = 1e7 + 10, mod = 1e9 + 7;

int t, n, m;
int s[N], f[N];
ll ans, k;
PII p[N];
string str;
stack<int> sta, stb;
map<int, int> ma;

void init() {
	for (int i = 1; i <= 10000000; i++) {
		s[i] = s[i / 10] + i % 10;
	}
	for (int k = 0; k <= 10; k++) {
		ans = 0;
		for (int i = 0; i <= k; i++) {
			for (int j = 0; j <= k - i; j++) {
				if (s[i] + s[j] + s[k-i-j] == s[k]) {
					ans++;
				}
			}
		}
		f[k] = ans;
	}
}

void sol() {
	cin >> n;
	ans = 1;
	while (n) {
		ans *= f[n%10];
		n /= 10;
	}
	cout << ans << endl;
}

int main()
{
	init();
	t = 1;
	cin >> t;
	while(t--)
		sol();
    return 0;
}

F - Shift and Reverse

题意:给出n,给出长度为n的数组,只能执行两种操作,操作1:整体翻转,操作2:将最后一个元素放到第一个位置,其他位置以此后移,问最少花费多少步操作可以使得非降序排列,恒不成立输出-1。

分析:可以直接看出,但凡能成立的一定是首尾相连后,从某个位置开始已经能非降序排列,或是非升序排列了,也可能两者同时成立,所以分别找,去min,如果是升序,找到最小值理论位于最左的那一个,比如1,1,2,3,4,最小值的最左位置就是第一个,1,1,2,3,1,最小值的最左位置其实就是第五个,说的是成立条件下的,所以要分类讨论一下,相反降序的话,找到最大值理论上最右的那一个,对于升序,找到位置后(当做x)也有两种方式,可以直接执行操作1,就是n-x+1,也可以先反转,就变成了将前面的往后移,最后再翻回来,就是2+x-1,取min(n-x+1,x+1),升序也是一样有两种min(n-x+2,x)。

代码:

#include <iostream> 
#include <algorithm>
#include <stack>
#include <map>
using namespace std;
typedef long long ll; 
typedef pair<int, int> PII;
const int N = 2e6 + 10, mod = 1e9 + 7;

int t, n, m;
int s[N], f[N], g[N];
int ans, k;
PII p[N];
string str;
stack<int> sta, stb;
map<int, int> ma;

void init() {
	
}

void sol() {
	ans = mod;
	cin >> n;
	int bj = 1;
	for (int i = 1; i <= n; i++) {
		cin >> s[i];
		s[i+n] = s[i];
		g[i] = s[i];
	}
	int ans = mod;
	sort(g + 1, g + n + 1);
	bool flag = true;
	bj = 1;
	if (s[1] == g[1]) {
		int j = n;
		while (s[j] == g[1]) {
			bj = j;
			j--;
		}
	} else {
		while (s[bj] != g[1]) bj++;
	}
	for (int i = bj + 1; i < bj + n; i++)
		if (s[i] < s[i-1])
			flag = false;
	if (flag) {
		if (bj == 1) ans = min(ans, 0);
		else ans = min(ans, min(n - bj + 1, 1 + bj));
	}
	flag = true;
	bj = 1;
	if (s[1] == g[n]) {
		int j = n;
		if (s[j] == g[n])
			while (s[j] == g[n]) {
				bj = j;
				j--;
			}
	} else {
		bj = n;
		for (int i = n; i >= 1; i--) 
			if (s[i] == g[n])
				bj = i;
	}
	for (int i = bj + 1; i < bj + n; i++) {
		if (s[i] > s[i-1])
			flag = false;
	}
	if (flag) {
		if (bj == 1) ans = min(ans, 1);
		else ans = min(ans, min(n - bj + 2, bj));
	}
	if (ans == mod) ans = -1;
	cout << ans << endl;
}

int main()
{
	init();
	t = 1;
	cin >> t;
	while(t--)
		sol();
    return 0;
}

G. Lights

题意:给出n,给出长度为n的01字符串,0代表关,1代表开,给出长度为n的a数组,每次对i号灯进行操作,会影响i号灯和a[i]号灯进行反转操作(即原先为关,操作后为开,原先为开,操作后为关),问是否可以在经过一系列操作后,使得所有灯都关掉,如果可能的话,输出操作次数,第二行输出需要对哪些灯进行操作,顺序不限,如果不可能,输出-1即可。

分析:
好比图中,对于题目的情况,每个点会连锁i和a[i],相当于画了一个图,图中可能会有多个类似图中的情况,要么就是一条链,要么就是多条链最终汇合到一个环中,而链的端点值没得选的,如果想改变状态就必须需要选择,所以记录每个点的入度,在最初入度为0的点放入队列中,然后依次去改值,这些是必须放的,做完这些后,最多只会剩下多个环,分别计算即可,如果任意一个环中不能全消掉,那直接不可能,输出-1即可,否则成立的话,你可以选择当前操作的点,也可以选择环中未操作的点(假设环为1-2-3-4-5-1,1和2是开着的灯,那么可以开1,也可以开2,3,4,5这四个,都可以达到全灭的效果),所以这里要判断一下怎么选,模拟走一遍记录环的总数和当前操作数即可。
代码(参考jiangly大佬的代码):

#include <iostream> 
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll; 
const int N = 2e5 + 10, mod = 1e9 + 7;

int t, n, m;
int s[N], deg[N];
string str;
queue<int> q;
vector<int> vec;

void init() {
	
}

void sol(int u) {
	cin >> n;
	cin >> str;
	for (int i = 0; i < n; i++) {
		deg[i] = 0;
	}
	for (int i = 0; i < n; i++) {
		cin >> s[i];
		s[i]--;
		deg[s[i]]++;
	}
	for (int i = 0; i < n; i++) {
		if (!deg[i])
			q.push(i);
	}
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		if (str[x] == '1') {
			str[x] = '0';
			str[s[x]] ^= 1;
			vec.push_back(x);
		}
		if (--deg[s[x]] == 0) {
			q.push(s[x]);
		}
	}
	for (int i = 0; i < n; i++) {
		if (deg[i]) {
			int j = i, now = 0, cnt = 0, sum = 0;
			do {
				sum++;
				now = (str[j] - '0') ^ now;
				if (now == 1) {
					cnt++;
				}
				j = s[j];
			} while(j != i);
			if (now == 1) {
				cout << -1 << endl;
				vec.clear();
				return;
			}
			j = i, now = 0;
			do {
				now = (str[j] - '0') ^ now;
				if (cnt < sum - cnt && now == 1) {
					vec.push_back(j);
				}
				else if (cnt >= sum - cnt && now == 0) {
					vec.push_back(j);
				}
				str[j] = '0';
				deg[j]--;
				j = s[j];
			} while(j != i);
		}
	}
	sort(vec.begin(), vec.end());
	cout << vec.size() << endl;
	for (int i = 0; i < vec.size(); i++)
		cout << vec[i] + 1 << ' ';
	cout << endl;
	vec.clear();
}

int main()
{
	init();
	t = 1;
	cin >> t;
	for (int i = 1; i <= t; i++)
		sol(i);
    return 0;
}
posted @   forleaves  阅读(129)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示