AtCoder Beginner Contest 267

AtCoder Beginner Contest 267

这次比赛出得真好,下次不要再出了。

代码建议按全屏查看!

A. Saturday

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define _pre(i, a, b) for(int i = (a); i >= (b); i--)

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

string s[20] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"}; 

signed main() {
	string k;
	cin >> k;
	_for (i, 0, 4) if (s[i] == k) cout << 6 - (i + 1);
}

B. Split?

这是一道质量很差的题目!!!出的真好!!!

我赛事什么思路都没想出来,于是直接想了个暴力。

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define _pre(i, a, b) for(int i = (a); i >= (b); i--)

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

char a[N];
int vis[N]; 

signed main() {
	cin >> (a + 1);
	int lena = strlen(a);
	if (a[1] == '1') puts("No"), exit(0); 
	if (a[4] == '0') {  // 如果第2列倒了,就看两边是否有 没有倒的球
		if ((a[7] == '1') && (a[8] == '1' || a[2] == '1' || a[5] == '1' || a[1] == '1' 
			|| a[9] == '1' || a[3] == '1' || a[6] == '1' || a[10] == '1')) puts("Yes"), exit(0);
	}
	if (a[8] == '0' && a[2] == '0') {  // 依次类推
		if ((a[7] == '1' || a[4] == '1') && (a[9] == '1' || a[5] == '1' ||
			 a[1] == '1' || a[3] == '1' || a[6] == '1' || a[10] == '1')) puts("Yes"), exit(0);
	}
	if (a[5] == '0' && a[1] == '0') {
		if ((a[7] == '1' || a[4] == '1' || a[2] == '1' || a[8] == '1') && 
			(a[9] == '1' || a[3] == '1' || a[6] == '1' || a[10] == '1')  ) puts("Yes"), exit(0);
	}
	if (a[9] == '0' && a[3] == '0') {
		if ((a[6] == '1' || a[10] == '1') && (a[7] == '1' || a[8] == '1' || a[4] == '1' || 
			a[5] == '1' || a[2] == '1' || a[1] == '1')) puts("Yes"), exit(0);
	}
	if (a[6] == '0') {
		if ((a[10] == '1') && (a[7] == '1' || a[4] == '1' || a[2] == '1' || a[1] == '1' || 
			a[8] == '1' || a[5] == '1' || a[3] == '1' || a[9] == '1') ) puts("Yes"), exit(0);;
	}
	puts("No");  // 如果没找到,输出No
}

C. Index × A(Continuous ver.)

求出一个长度 \(m\) 连续的序列的最大价值。(价值就是那一串 \(∑\)

第一眼很难,于是我们可以用一个滑动窗口的算法来解决问题:

  • 第一步:先求出区间 \(1\sim m\) 的价值,可以 \(O(n)\) 计算。
  • 第二步:求出区间 \(2\sim m+1\) 的价值,可以 \(O(1)\) 计算吗?
    • 因为区间 \([2,m+1]\) 跟区间 \([1, m]\) 相比,就滑动了一个位置,然后产生了什么贡献?
    • 不妨对比下列式子:
      \(a_1+a_2\times 2+a_3\times 3+....+a_{m-1}\times (m-1)+a_m\times m\),这是区间 \([1,m]\) 的价值。
      \(a_2+a_3\times 2+a_4\times 3+....+a_m\times (m-1) + a_{m+1}\times m\),这是区间 \([2,m+1]\) 的价值。
    • 看出来了吗,多了什么?少了什么?容易发现多了 \(a_{m+1}\times m\),少了 \(∑_{i=1}^ma_i\)
    • 这些东西都可以进行 \(O(1)\) 计算。
    • 也就是得到一点:\([l,r]\) 可以 \(O(1)\) 得到 \([l+1,r+1]\)
  • 第三步:是不是可以一直求到 \([n-m+1,n]\)?显然可以。

于是可以得到下面的代码,复杂度 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define _pre(i, a, b) for(int i = (a); i >= (b); i--)

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

int n, m, a[N], t, res = -1e18, sum[N];

signed main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> a[i], sum[i] = sum[i - 1] + a[i];
	for (int i = 1; i <= m; i++) t += i * a[i]; res = max(res, t);
	for (int i = 2; i <= n - m + 1; i++) {
		t += a[i + m - 1] * m;  // 多的
		t -= (sum[i + m - 2] - sum[i - 2]);  // 少的
		res = max(res, t);
	}
	cout << res;
}

D. Index × A(Not Continuous ver.)

定义 \(dp_{i,j}\) 表示前 \(i\) 个数选了 \(j\) 个数的最大价值。转移看第 \(i\) 个数选不选即可。过于简单,代码略。

E. Erasing Vertices 2

最大值的最小值,我们二分最小值,则我们只能进行不超过该最小值成本的操作,记为 \(mid\)。显然有些点删除会超过 \(mid\),那怎么解决呢?

  • 每一个点删除都有成本,这个成本可以提前计算。删除 \(x\) 点的成本记为 \(w_x\),点 \(x\) 的权值即为 \(a_x\)(题目中有 \(a_x\) 定义)

  • 考虑有些点删除之后成本是不大于 \(mid\) 的,那么这些点都可以删除。那么这些可以直接删除的点删除之后会不会有什么影响?删除一个点,就会对其直接相连的点产生影响。

  • \(x\) 点已经消失了,与它直接相连的 \(v\) 点,那么 \(w_v\) 的成本就会减少 \(a_x\)。也就是如果删除 \(x\) 点免费,那就直接删了吧,删了会有好处,\(w_v\) 成本会减少!!
    如果说 \(w_v\) 的成本减少到可以直接免费删除,也就是 \(w_v\leq mid\),那 \(v\) 点也不就可以免费删除了吗??

  • 所以:删除 \(x\) 点可能会使得 \(v\) 点也可以直接删除,\(v\) 点删除之后可能导致其他点也可以直接删除,这相当于一个连环效应。具体怎么实现看代码。

代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 4e5 + 5;
int n, m, a[N], w[N], t[N];  
// wi表示删掉这个点的花费 

int en, first[N];
struct edge {
	int e, next;
}ed[N];

void add_edge(int s, int e) {
	en++;
	ed[en].e = e;
	ed[en].next = first[s];
	first[s] = en;
}

void dfs(int x, int fa, int mid) {  // dfs相当于连环删除
	t[x] = -1e9;  // x点已被删除,成本对它没用了
	for (int p = first[x]; p; p = ed[p].next) {
		int e = ed[p].e;
		if (e == fa || t[e] <= 0) continue; // 如果说e点被删了,就不用继续更新了
		t[e] -= a[x];
		if (t[e] <= mid) dfs(e, x, mid);  // 如果x号点可以使得e点直接删除,那就删!
	}
}

bool check(int x) {
	for (int i = 1; i <= n; i++) t[i] = w[i];
	for (int i = 1; i <= n; i++) {
		if (t[i] >= 0 && t[i] <= x) dfs(i, 0, x);  // 如果说i号点可以删,那就删
	}
	for (int i = 1; i <= n; i++) if (t[i] > x) return 0;
	return 1;
}

signed main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> a[i];
	while (m--) {
		int u, v;
		cin >> u >> v;
		w[u] += a[v], w[v] += a[u];
		add_edge(u, v), add_edge(v, u);
	}
	int l = 0, r = 1e18;
	while (l < r) {
		int mid = l + r >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}
	cout << l << endl;
} 

F. Exactly K Steps

模板题,树上跳 \(K\) 步。

换根法,把最深的儿子当成根来 dfs,这样处理的倍增数组都不用担心转折点的问题。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int n, m, s, rr, child;

int en, first[N], dep[N], dep1[N], dep2[N], f[N][21], down[N][21];
struct edge {
	int e, next;
}ed[N];

void add_edge(int s, int e) {
	en++;
	ed[en].e = e;
	ed[en].next = first[s];
	first[s] = en;
}

void dfs(int x, int fa) {
	dep[x] = dep[fa] + 1;
	for (int p = first[x]; p; p = ed[p].next) {
		int e = ed[p].e;
		if (e != fa) {
			dfs(e, x);
		}
	}
}

void dfs1(int x, int fa) {
	dep1[x] = dep1[fa] + 1;  // 往上搜的深度 
	down[x][0] = fa;
	for (int j = 1; j <= 20; j++) {
		down[x][j] = down[down[x][j - 1]][j - 1];
	}
	for (int p = first[x]; p; p = ed[p].next) {
		int e = ed[p].e;
		if (e != fa) dfs1(e, x);
		}
	}
} 

void dfs2(int x, int fa) {
	dep2[x] = dep2[fa] + 1;  // 往下搜的深度 
	f[x][0] = fa;
	for (int j = 1; j <= 20; j++) {
		f[x][j] = f[f[x][j - 1]][j - 1];
	}
	for (int p = first[x]; p; p = ed[p].next) {
		int e = ed[p].e;
		if (e != fa) dfs2(e, x);
	}
} 

int main() {
	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		add_edge(x, y), add_edge(y, x);
	}
	cin >> m;
	dfs(1, -1);
	int root = max_element(dep + 1, dep + n + 1) - dep;
	dfs1(root, -1);
	root = max_element(dep1 + 1, dep1 + n + 1) - dep1;
	dfs2(root, -1);	
	while (m--) {
		int a, k;
		cin >> a >> k;
		if (dep1[a] <= k && dep2[a] <= k) puts("-1");
		else if (dep1[a] > k) {
			for (int i = 19; i >= 0; i--) {
				if (k >> i & 1) a = down[a][i];
			}
			cout << a << endl; 
		}
		else {
			for (int i = 19; i >= 0; i--) {
				if (k >> i & 1) a = f[a][i];
			}
			cout << a << endl; 
		}
	}
} 

G. Increasing K Times

待补。

posted @ 2022-09-05 14:12  Otue  阅读(48)  评论(0编辑  收藏  举报