AtCoder Beginner Contest 157 题解报告

A:Duplex Printing

题目大意 :

给你一个数,一次能打印两面,问表示需要打印多少张才能将所有打印完。

析题得侃 :

如果这个数是偶数, 需要打印 n / 2
如果这个数是奇数, 需要打印 n / 2 + 1.

考察点:

签到,思维

Code:

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

#define INF 0x3f3f3f3f

using namespace std;

typedef long long LL;
typedef pair<int,int>PII; 

int main(void) {
	int n;
	cin >> n;
	if(n & 1) cout << n / 2 + 1 << endl;
	else cout << n / 2 << endl;
	return 0;
}

B:Bingo

题目大意:

给一个 3 * 3 的矩阵,每个位置有一个数,之后再给你一些数,如果这些数在这个矩阵中
并且所以数组合到一起可以使得 有一列或者有一行或者斜对角 都出现过,就输出 Yes,
否则输出 No.

析题得侃:

没有想到好的方法,就一个一个枚举了,hh,方法虽然笨点,总归是 AC 了。

考察点 :

模拟

Code :

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

#define INF 0x3f3f3f3f

using namespace std;

typedef long long LL;
typedef pair<int,int>PII; 

int a[5][5];
int cnt[5][5];

int main(void) {
	for(int i = 1; i <= 3; i ++) {
		for(int j = 1; j <= 3; j ++) {
			cin >> a[i][j];
		}
	}
	int n,value;
	cin >> n;
	while(n --) {
		cin >> value;
		bool vis = false;
		for(int i = 1; i <= 3; i ++) {
			for(int j = 1; j <= 3; j ++) {
				if(a[i][j] == value) {
					vis = true;
					cnt[i][j] = 1;
					break;
				}
			}
			if(vis) break;
		}
	}
	if((cnt[1][1] == 1 && cnt[1][2] == 1 && cnt[1][3] == 1) || (cnt[2][1] == 1 && cnt[2][2] == 1 && cnt[2][3] == 1) ||
		(cnt[3][1] == 1 && cnt[3][2] == 1 && cnt[3][3] == 1) || (cnt[1][1] == 1 && cnt[2][1] == 1 && cnt[3][1] == 1) ||
		(cnt[1][2] == 1 && cnt[2][2] == 1 && cnt[3][2] == 1) || (cnt[1][3] == 1 && cnt[2][3] == 1 && cnt[3][3] == 1) ||
		(cnt[1][1] == 1 && cnt[2][2] == 1 && cnt[3][3] == 1) || (cnt[1][3] == 1 && cnt[2][2] == 1 && cnt[1][3] == 1)
	) {
		cout << "Yes" << endl;
	} else {
		cout << "No" << endl;
	}
	return 0;
}

C:Guess The Number

题目大意:

大概就是说给你一个不超过三位的一个数,然后这之后会有一些操作,可以指定某一位是多少,
但是不允许有前导 0 ,同一个位置不能重复指定,否则会出现错误,除了指定的数外,还要保证
最后得到的数是最小的。

析题得侃:

题是不难,就是细节忒多,需要考虑的情况比较多,这道题我还犯了一个小错误,就是重复输出了,
对于某一种情况没有及时跳出来,导致对某一个测试样例会输出两个答案。不过这道题收获还是
蛮多的,重要的是学会尝试着去找不同的情况去接近答案。

考察点:

思维,模拟

Code:

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

#define INF 0x3f3f3f3f

using namespace std;

typedef long long LL;
typedef pair<int,int>PII;

int n,m;

int x,b;

int a[4],cnt[4];

int main(void) {
	cin >> n >> m;
	if(n == 1 && m == 0) {
		// 这种情况要及时跳出来,不然就与下面的冲突了 
		cout << 0 << endl;
		return 0;
	}
	bool vis = true;
	while(m --) {
		cin >> x >> b;
		if(vis == false) continue;
		if(n > 1 && x == 1 && b == 0) {
			vis = false;
		} else if(cnt[x] && a[x] != b) {
			vis = false;
		} else {
			a[x] = b;
			cnt[x] = 1;
		}
	}
	if(vis == false) {
		cout << -1 << endl;
	} else {
		for(int i = 1; i <= n; i ++) {
			if(a[1] == 0 && cnt[1] == 0) a[1] = 1;
			cout << a[i] ;
		}
		cout << endl;
	}
	return 0;
}

D:Friend Suggestions

题目大意:

先给一些关系,表明两个数之间是朋友的关系,接着再给一些关系,表示这些人之间不是朋友的
关系,最后问你每个人可以有多少个人可以是自己的候选朋友(不包括已有的),而且所有人跟
自己还必须是在同一个连通块内的。

析题得侃:

这题是赛后补的,琢磨完第三题剩下的时间就不多了,跟一些大佬真的没法比,不过最后靠自己
的能力把这道题补出来,自己还是很欣慰的。
一看到这道题涉及到关系,第一时间想到的就是并查集,然后就往哪里想,实际上是带权并查集
的初级版,我们最后求得是候选的朋友,我们可以求出这个连通块的大小,然后每个人的朋友就是
当前这个点的出度,还有在同一个连通块中跟这个人也有可能不是朋友,这是我们可以拿一个
cnt 数组用来计算不是朋友的数量(是双向的,具体的会在代码中体现),然后这个连通块的大小
- 朋友数量 - 不是朋友的数量 = 候选朋友的数量(同一个连通块内)。

考察点:

带权并查集,连通块

Code:

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

#define INF 0x3f3f3f3f

using namespace std;

typedef long long LL;
typedef pair<int,int>PII;

const int maxn = 1e5 + 10;

int vis[maxn],cnt[maxn];
int fa[maxn],sz[maxn],deg[maxn];

int ans = 0;

int get(int x) {
	return x == fa[x] ? x : fa[x] = get(fa[x]);
}

void Union(int x,int y) {
	int xx = get(x);
	int yy = get(y);
	if(xx == yy) return ;
	fa[yy] = xx;
	sz[xx] += sz[yy];
	return ;
}

int main(void) {
	int n,m,k;
	int u,v;
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i ++) {
		fa[i] = i;
		// 刚开始每个都是独立的,都是一个 
		sz[i] = 1;

	}
	for(int i = 1; i <= m; i ++) {
		cin >> u >> v;
		// 记录每个点的出度 
		deg[u] ++;
		deg[v] ++;
		Union(u,v);
	}
	for(int i = 1; i <= k; i ++) {
		cin >> u >> v;
		if(get(u) == get(v)) {
			// 不是朋友,但在同一个连通块内,所以都需要减少 
			cnt[u] --;
			cnt[v] --;
		}
	}
	for(int i = 1; i <= n; i ++) {
		cout << sz[get(i)] - deg[i] + cnt[i] - 1<< " ";
	}
	return 0;
}

E:Simple String Queries

题目大意:

有一串字符,我们可以执行两种操作:
1、替换某个位置的字符
2、查询某一段区间不同字符的个数

析题得侃:

很明显:这道题的两种操作分别是:单点修改,区间查询
是不是跟树状数组的基本操作十分的类似,当然,线段树也是可以的,但是杀鸡焉用牛刀,
关键是自己太菜,哈哈。我们可以用一个二维的数组记录当前位置之前每个字符出现的数量。
具体实现看代码。

考察点:

树状数组,线段树

Code:

#include <cstdio> 
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 5e5 + 10;

char a[maxn];

int c[maxn][26];

int t,op,pos,l,r,len;
char v;

int lowbit(int x) {
	return x & -x;
}

void insert(int id,int x,int v) {
	for(int i = id; i <= len; i += lowbit(i)) {
		c[i][x] += v;
	}
	return ;
}

int query(int id,int x) {
	int ans = 0;
	// x 代表是( x - 'a') 
	for(int i = id; i; i -= lowbit(i)) {
		ans += c[i][x];
	}
	return ans;
}

int main(void) {
	cin >> len;
	scanf("%s",a + 1);
	for(int i = 1; i <= len; i ++) {
		int v = a[i] - 'a';
		insert(i,v,1);
	}
	cin >> t;
	while(t --) {
		cin >> op; 
		if(op == 1) {
			cin >> pos >> v;
			// 更新 
			insert(pos,a[pos] - 'a',-1);
			insert(pos,v - 'a',1);
			a[pos] = v;                    // 修改后记得修改原数组 
		} else {
			cin >> l >> r;
			int res = 0;
			for(int i = 0; i < 26; i ++) {
				// 如果这个区间有这个字符,只需要 + 一次即可 
				if(query(r,i) - query(l - 1,i)) res ++;
			}
			cout << res << endl;
		}
	}
	return 0;
}

F:Yakiniku Optimization Problem

待补

后记:

本人能力有限,写的不当之处还望各位看官多多指教,如在参考中遇到各种问题,欢迎大家
留言交流,共同交流。
posted @ 2020-03-02 22:40  IceSwords  阅读(416)  评论(0编辑  收藏  举报