AtCoder Beginner Contest 401 B~E

AtCoder Beginner Contest 401 B~E

B - Unauthorized(模拟)

代码

#include <iostream>
#define endl '\n'
using namespace std;
int n, m, k;
void solve() {
	string op;
	int n; cin >> n;
	int flag = 0;		// 记录当前是登录状态还是注销状态
	int cnt = 0;
	for (int i = 0; i < n; i++) {
		cin >> op;
		if (op == "login") {
			flag = 1;
		}
		else if (op == "logout") {
			flag = 0;
		}
		else if (op == "private") {	
			if (flag == 0) {		// 注销状态访问私人内容
				cnt++;
			}
		}
	}
	cout << cnt;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int _; _ = 1;
	while (_--) {
		solve();
	}
	return 0;
}

C - K-bonacci(前缀和、同余原理)

题意

  • 一个长度为n+1的数组,前面k个是1,后面的数Ai就是i往前数k个数的和

思路

  1. 要求前面k个数的和,所以就可以搞个前缀和了,算的时候同余一下
  2. n<k直接输出1就行,因为连第k+1个数都没有

代码

#include <iostream>
#include <vector>
#define endl '\n'
using namespace std;
const int mod = 1e9;
int n, m, k;

void solve() {
	cin >> n >> k;
	if (n < k) {
		cout << 1;
		return;
	}
	vector<long long>a(n + 1);
	vector <long long>sumA(n + 1);
	sumA[0] = 1;
	for (int i = 0; i < k; i++) {
		a[i] = 1;
		if (i == 0)continue;
		sumA[i] = (sumA[i - 1] + a[i]) % mod;
	}
	a[k] = sumA[k - 1];
	sumA[k] = sumA[k - 1] + a[k];
	for (int i = k + 1; i <= n; i++) {
		a[i] = (sumA[i - 1] - sumA[i - k - 1] + mod) % mod;
		sumA[i] = (sumA[i - 1] + a[i]) % mod;
	}
	cout << a[n];
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int _; _ = 1;
	while (_--) {
		solve();
	}
	return 0;
}
// n==4,k==2
// 1 1 2 3 5

D - Logical Filling(构造/模拟,思维)

题意

  1. 给长度为n的字符串S(只包含o . ?),把 ? 替换乘 o 或者 . 然后里面有k个o,然后o不能相邻
  2. 构造出来的字符串,如果问号的地方是确定是某个字符(o或者.),那就确定了,否则就还是 ?

思路

  1. 首先可以确定原本的字符串S当中,如果 o 周围有 ? ,那一定就填 . 因为不能是 o (会相邻)
  2. 可以算出来还要填demand个 o (填在 ? 的地方)
  3. 如何算 ? 怎么填 o
  • 分段计算,对于每一个连续的问号段,问号数量为empt,那最多可以填 o 的数量provide就是

    (empt + 1)/ 2,这个不相邻放置的问题还是经常遇到的

  • 如果某一段问号段是奇数个问号,那填入provide个 o 的方法是固定的,如果是偶数个,那就有两种方法

  1. 计算完provide的总和,
    • 如果provide>demand,那怎么放,完全不知道,所以剩下问号的位置都还是问号
    • 如果provide==demand,那就按照前面奇偶的那里分析,来处理 ? 地方填什么(偶数段可以确定怎么放,而奇数段没法确定,所以都还是问号)

代码

#include <iostream>
#include <string>
#define endl '\n'
using namespace std;
int n, m, k;
void solve() {
	cin >> n >> k;
	string s;
	cin >> s;
	int cnt = 0;
	// 首先可以确定一部分.的位置(o的相邻位置一定是.)
	for (int i = 0; i < s.length(); i++) {
		if (s[i] == 'o') {
			if (i && s[i - 1] == '?') {
				s[i - 1] = '.';
			}
			if (i < s.length() - 1 && s[i + 1] == '?') {
				s[i + 1] = '.';
			}
			cnt++;
		}
	}
	int demand = k - cnt;
	// 如果o的数量已经等于k,那就把剩下的?都输出.
	if (demand == 0) {
		for (auto& ch : s) {
			if (ch == '?')cout << ".";
			else cout << ch;
		}
		return;
	}

	// 对于每一段连续的问号,如果数量是奇数,那就最多可以放(empt+1)/2个o
	// 奇数的时候放法固定(前提所有能放o的位置刚刚好全部放满o)
	// 如果是偶数,放法不固定,这一段问号不能确定
	int provide = 0;	// 最多可以放多少个o
	int empt = 0;		// 每一段连续的问号的数量

	for (int i = 0; i < s.length(); i++) {
		empt = 0;
		int j = i;
		for (; j < s.length(); j++) {
			if (s[j] == '?') {
				empt++;
			}
			else {
				break;
			}
		}				
		i = j;
		provide += (empt + 1) / 2;
	}

	if (provide == demand) {
		for (int i = 0; i < s.length(); i++) {
			empt = 0;
			int j = i;
			for (; j < s.length(); j++) {
				if (s[j] == '?') {
					empt++;
				}
				else {
					break;
				}
			}
			if ((empt & 1) == 1) {			// 连续问号段是奇数,要确定放法
				for (int k = 0; k < empt; k++) {
					if ((k & 1) == 0) {		// 这里是连续问号段中第奇数个问号
						s[i + k] = 'o';
					}
					else {
						s[i + k] = '.';
					}
				}
			}
			i = j;
		}
	}
	cout << s;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int _; _ = 1;
	while (_--) {
		solve();
	}
	return 0;
}

E - Reachable Set(并查集)

题意

  1. 给无向图,以1~n选一个k为最大节点,从1~k的节点都要包含,最后只能保留一个连通块,看最少删除几条边,可以连通出来,如果无解就输出-1

思路

  • 枚举从小到大枚举k,然后遍历k的所有边,

    • 如果有大于k的节点v,那就要删掉这个节点v,等到k枚举到v的时候,再放出来

    • 如果是v<=k,那就连通

代码

#include <iostream>
#include <vector>
using namespace std;
#define endl '\n'
const int N = 2e5 + 5;
int n, m, cnt;
vector<int>ans, par, dele;
vector<vector<int>>adj;
int find(int x) {
	if (par[x] == x)return x;
	return par[x] = find(par[x]);
}

void merge(int x, int y) {
	int parx = find(x);
	int pary = find(y);
	if (parx != pary) {
		cnt--;
	}
	par[pary] = parx;
}

void init() {
	cin >> n >> m;
	par.resize(n + 1);
	dele.resize(n + 1);
	adj.resize((n + 1), vector<int>());
	for (int i = 1; i <= n; i++) {
		par[i] = i;
	}
	int u, v;
	for (int i = 0; i < m; i++) {
		cin >> u >> v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}
}

void solve() {
	init();
	// 以1~n为最大节点,看每次最少有多少个连通块,如果最少为1,那就合法
	int res = 0;
	for (int k = 1; k <= n; k++) {
		cnt++;
		if (dele[k]) {
			dele[k] = 0;
			res--;
		}
		for (auto v : adj[k]) {
			if (v > k) {
				if (dele[v]) continue;		// 已经不在连通块里面了
				dele[v] = 1;
				res++;
			}
			else {
				if (dele[v]) {
					dele[v] = 0;
					res--;
				}
				merge(v, k);
			}
		}
		ans.push_back((cnt == 1) ? res : -1);
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int _ = 1;
	//cint>>_;
	while (_--) {
		solve();
	}
	for (auto x : ans)cout << x << endl;
	return 0;
}
posted @ 2025-04-15 00:54  zombieee  阅读(25)  评论(0)    收藏  举报