AtCoder Beginner Contest 283 A-F 题解

比赛链接

A - Power

快速幂板子

#include<cstdio>
#include<algorithm>
using namespace std;

#define int long long

int n, m;

int qpow(int a, int b) {
	int res = 1;
	while(b) {
		if(b & 1) res *= a;
		a *= a, b >>= 1;
	}
	return res;
}

signed main() {
	scanf("%lld %lld", &n, &m);
	printf("%lld", qpow(n, m));
	return 0;
}

B - First Query Problem

跟着模拟就好,该修改就修改,该输出就输出。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
//#define int long long

int n, m, a[N];

int qpow(int a, int b) {
	int res = 1;
	while(b) {
		if(b & 1) res *= a;
		a *= a, b >>= 1;
	}
	return res;
}

signed main() {
	
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	scanf("%d", &m);
	int op, x, y;
	while(m --) {
		scanf("%d", &op);
		if(op == 1) {
			scanf("%d %d", &x, &y);
			a[x] = y;
		}
		else scanf("%d", &x), printf("%d\n", a[x]);
	}
	return 0;
}

C - Cash Register

倒着处理,如果数字为 1 ~ 9,加 1,

判断是否有 00,如果有,只加 1。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
//#define int long long

int n, ans, a[N];

int qpow(int a, int b) {
	int res = 1;
	while(b) {
		if(b & 1) res *= a;
		a *= a, b >>= 1;
	}
	return res;
}

char s[N];

signed main() {
	
	scanf("%s", s + 1);
	n = strlen(s + 1);
	
	for(int i = n; i; i --) {
		if(i > 1 && s[i] == s[i - 1] && s[i] == '0') i --;
		ans ++;
	}
	printf("%d", ans);
	return 0;
}

D - Scope

因为题目保证输入字符串为合法,所以预处理出每个左右括号匹配的区间,st[i] ~ ed[i] (表示 s[i] 为 '(' 时的匹配区间)

因为是从左到右处理过来的,所以在 i 之前的括号区间肯定都处理过,因此可以直接跳过,

再拿个 bool 数组记录一下字母是否出现,完了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 3e5 + 5;
//#define int long long

int n, ans, a[N], st[N], ed[N], top, sta[N];

char s[N];

bool vis[N], tag[N];

signed main() {
	
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for(int i = 1; i <= n; i ++) {
		if(s[i] == '(' ) sta[++top] = i;
		if(s[i] == ')') {
			ed[sta[top]] = i;
			st[i] = sta[top];
			top --;
		}
	}
	
	for(int i = 1; i <= n; i ++) {
		if(s[i] != '(' && s[i] != ')') {
			if(vis[s[i] - 'a']) {
				puts("No");
				return 0;
			}
			vis[s[i] - 'a'] = 1;
		}
		else if(s[i] == ')'){
			for(int j = st[i] + 1; j <= i; j ++) {
				while(ed[j] && j <= i) j = ed[j] + 1;
				if(j > i) break;
				if(s[j] != '(' && s[j] != ')') vis[s[j] - 'a'] = 0;
			}
		}
	}
	puts("Yes");
	return 0;
}

E - Don't Isolate Elements

操作每次处理的是一行,因为每行有两种状态,A[i][j] or 1 - A[i][j]

分析可知,每行的可行性只与上下相邻的两行有关,

因此,定义状态 dp[i][j(0/1)][k(0/1)] 为 第 i - 1 行为 j 状态, 第 i 行为 k 状态时满足题意的最小操作数。

不难得出,dp[i][j][k] = min(dp[i - 1][h][j] + k) (h 为枚举的 i - 2 行的状态)

但需要判断三行状态为 h,j,k 时的合法性,

所以还要再写一个 check() 函数,特别注意 第 1 行 和 第 n 行的特殊性,需要单独判断、

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1005;
//#define int long long

int n, m, ans = 1e9, s[N][N];

int dp[N][2][2];

bool check(int d, int now, int low, int up) {
	int tmp;
	if(d == 1) {
		for(int i = 1; i <= m; i ++) {
			tmp = 0;
			if((s[d][i] ^ now) == (s[d + 1][i] ^ low)) tmp ++;
			if(i > 1 && s[d][i] == s[d][i - 1]) tmp ++;
			if(i < m && s[d][i] == s[d][i + 1]) tmp ++;
			if(!tmp) return 0;
		}
		return 1;
	}
	for(int i = 1; i <= m; i ++) {
		tmp = 0;
		if((s[d][i] ^ now) == (s[d + 1][i] ^ low) || (s[d][i] ^ now) == (s[d - 1][i] ^ up)) tmp ++;
		if(i > 1 && s[d][i] == s[d][i - 1]) tmp ++;
		if(i < m && s[d][i] == s[d][i + 1]) tmp ++;
		if(!tmp) return 0;
	}
	if(d == n - 1) {
		for(int i = 1; i <= m; i ++) {
			tmp = 0;
			if((s[n][i] ^ low) == (s[d][i] ^ now)) tmp ++;
			if(i > 1 && s[n][i] == s[n][i - 1]) tmp ++;
			if(i < m && s[n][i] == s[n][i + 1]) tmp ++;
			if(!tmp) return 0;
		}
	}
	return 1;
}

signed main() {
	
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= m; j ++)
			scanf("%d", &s[i][j]);
	}
	memset(dp, 0x3f, sizeof(dp));
	dp[1][0][0] = dp[1][1][0] = 0, dp[1][1][1] = dp[1][0][1] = 1;
	
	for(int i = 2; i <= n; i ++) {
		for(int a = 0; a < 2; a ++) {
			for(int b = 0; b < 2; b ++) {
				for(int c = 0; c < 2; c ++) {
					if(!check(i - 1, a, b, c)) continue;
					dp[i][a][b] = min(dp[i][a][b], dp[i - 1][c][a] + b);
				}
			}
		}
	}
	for(int i = 0; i < 2; i ++) {
		for(int j = 0; j < 2; j ++) {
			ans = min(dp[n][i][j], ans);
		}
	}
	
	if(ans == 1e9) puts("-1");
	else printf("%d",ans);
	
	return 0;
}

F - Permutation Distance

考虑把绝对值去掉,并且将 \(P_i\)\(i\)\(P_j\)\(j\) 分别放到一边。

那么就需要分类讨论:

  • \(P_i + i + min(-P_j - j) (P_i > P_j, i > j)\)
  • \(P_i - i + min(-P_j + j) (P_i > P_j, i < j)\)
  • \(-P_i + i + min(P_j - j) (P_i < P_j, i > j)\)
  • \(-P_i - i + min(P_j + h) (P_i < P_j, i < j)\)

于是问题就转化为求两个不等关系条件下的最小值(二维偏序),

我们就可以循环 \(i \in [1, n]\) 来控制一个不等条件,另一个条件用数据结构维护(线段树——单点修改,区间查询)。

即可求出最值。

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

const int N = 2e5 + 5, inf = 1e9;

struct node {
	int l, r, a, b;
	node(){}
	node(int L, int R, int A, int B) {
		l = L, r = R, a = A, b = B;
	}
}tr[N << 4];

int n, A[N];

#define ls k << 1
#define rs k << 1 | 1

void pushup(int k) {
	tr[k].a = min(tr[ls].a, tr[rs].a);
	tr[k].b = min(tr[ls].b, tr[rs].b);
}

node merge(node x, node y) {
	node res = node(x.l, y.r, min(x.a, y.a), min(y.b, x.b));
	return res;
} 

node query(int k, int st, int ed) {
	if(tr[k].l >= st && tr[k].r <= ed) return tr[k];
	int mid = (tr[k].l + tr[k].r) >> 1;
	node res;
	if(ed <= mid) return query(ls, st, ed);
	else if(st > mid) return query(rs, st, ed);
	else return merge(query(ls, st, ed), query(rs, st, ed)); 
}

void change(int k, int pos, int a, int b) {
	if(tr[k].l == tr[k].r) {
		tr[k].a = a, tr[k].b = b;
		return;
	}
	int mid = (tr[k].l + tr[k].r) >> 1;
	if(pos <= mid) change(ls, pos, a, b);
	else change(rs, pos, a, b);
	pushup(k);
}

void build(int k, int l, int r) {
	if(l == r) {
		tr[k] = node(l, r, inf, inf);
		return;
	}
	tr[k].l = l, tr[k].r = r;
	int mid = (l + r) >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
	pushup(k);
}
int ans[N];
int main() {
	scanf("%d", &n);
	memset(ans, 0x3f, sizeof(ans));
	for(int i = 1; i <= n; i ++) {
		scanf("%d", &A[i]);
	}
	build(1, 1, n);
//	puts("*2");
	for(int i = 1; i <= n; i ++) {
		if(A[i] > 1) ans[i] = min(ans[i], A[i] + i + query(1, 1, A[i] - 1).a);
		if(A[i] < n) ans[i] = min(ans[i], i - A[i] + query(1, A[i] + 1, n).b);
		change(1, A[i], - A[i] - i, A[i] - i);
		
	}
	build(1, 1, n);
	for(int i = n; i; i --) {
		if(A[i] > 1) ans[i] = min(ans[i], A[i] - i + query(1, 1, A[i] - 1).a);
		if(A[i] < n) ans[i] = min(ans[i], - i - A[i] + query(1, A[i] + 1, n).b);
		change(1, A[i], - A[i] + i, A[i] + i);
	}
	for(int i = 1; i <= n; i ++) printf("%d ", ans[i]);
	return 0;
}
posted @ 2023-02-24 22:39  Spring-Araki  阅读(13)  评论(0编辑  收藏  举报