比赛链接:

https://atcoder.jp/contests/abc266

C - Convex Quadrilateral

题意:

平面图上有一个四边形,按照逆时针顺序给定四个点的坐标,判断四边形是不是凸的。

思路:

求两条临边的向量积是不是 > 0 即可。

代码:

#include <bits/stdc++.h>
using namespace std;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	vector <int> x(4), y(4);
	for (int i = 0; i < 4; i ++ )
		cin >> x[i] >> y[i];
	for (int i = 0; i < 4; i ++ ){
		int a = (x[(i - 1 + 4) % 4] - x[i]) * (y[(i + 1) % 4] - y[i]) - (x[(i + 1) % 4] - x[i]) * (y[(i - 1 + 4) % 4] - y[i]);
		if (-a <= 0){
			cout << "No\n";
			return 0;
		}
	}
	cout << "Yes\n";
	return 0;
}

D - Snuke Panic (1D)

题意:

地上有五个连续的坑,序号从 0 到 4,有 \(n\) 件物品会掉落,第 \(i\) 件物品会在 \(t_i\) 秒掉到 \(x_i\) 坑,它的体积是 \(a_i\),角色 0 秒的时候在 0 号坑,每一秒可以移动一步,问最多能接到体积总和为多少的物品。

思路:

因为上一秒的情况转移到这一秒是有限的,且没有后效性,考虑 \(dp\)
\(dp[i][j]\) 表示第 \(i\) 秒到 \(j\) 号坑能获得体积为多少的物品。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10;
LL dp[N][5], p[N], w[N];
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++ ){
		int t, x, a;
		cin >> t >> x >> a;
		p[t] = x;
		w[t] = a;
	}
	for (int i = 1; i <= 4; i ++ ){  //刚开始到不了这四个点
		dp[0][i] = -1e18;
	}
	for (int t = 1; t <= 100000; t ++ ){
		for (int i = 0; i < 5; i ++ ){
			dp[t][i] = dp[t - 1][i];
			if (i != 0) dp[t][i] = max(dp[t][i], dp[t - 1][i - 1]);
			if (i != 4) dp[t][i] = max(dp[t][i], dp[t - 1][i + 1]);
		}
		dp[t][p[t]] += w[t];
	}
	LL ans = 0;
	for (int i = 0; i <= 4; i ++ )
		ans = max(ans, dp[100000][i]);
	cout << ans << "\n";
	return 0;
}

E - Throwing the Die

题意:

摇骰子,最多可以摇 \(n\) 轮,每回合,可以选择结束,那么当前摇出来的值就是最终的分数,也可以继续摇,问最后的最大期望分数是多少。

思路:

每次只有两个选择,继续或者结束,容易想到第 \(i\) 轮从第 \(i - 1\) 轮转移过来。
\(f[i]\) 表示进行了 \(i\) 轮的最大期望分数。
那么第 \(i\) 轮的最大分数就是第 \(i - 1\) 轮的最大分数或者当前这一轮的结束分数。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n;
	cin >> n;
	function<double(int)> f = [&](int x){
		if (x == 0) return 0.0;
		double t = f(x - 1), res = 0;
		for (int i = 1; i <= 6; i ++ )
			res += max(t, i * 1.0) / 6;
		return res;
	};
	cout << fixed << setprecision(15) << f(n) << "\n";
	return 0;
}

F - Well-defined Path Queries on a Namori

题意:

给定 \(n\) 个节点,\(n\) 条双向边的图,\(q\) 次询问,问 \(u\)\(v\) 的简单路径是不是只有 1 条。

思路:

\(n\) 个节点,\(n\) 条双向边的图的本质就是一棵树 + 一个环,所以先想办法把环弄出来,当度为 1,就将它入队,用 \(bfs\) 跑出来环。
当询问的两个点都在环上,显然不止一条简单路径。
当两个点不在环上,显然就一条简单路径。
当一个在环上一个不在,可能是可能不是。

红色和蓝色只有一条简单路径,绿色和蓝色有两条。
考虑在 \(bfs\) 的时候加一个 \(dsu\),将环上一个节点和与它连通的不在环上的节点联通起来,这样子只用判断是不是在一个连通块里面就行了。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
struct dsu{
	int n;
	vector <int> p;
	dsu(int n) : n(n){
		p.resize(n + 1);
		iota(p.begin(), p.end(), 0);
	}
	int get(int x){
		return (x == p[x] ? x : (p[x] = get(p[x])));
	}
	void unite(int x, int y){
		x = get(x);
		y = get(y);
		if (x != y){
			p[x] = y;
		}
	}
};
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n;
	cin >> n;
	vector < vector<int> > e(n + 1);
	vector <int> deg(n + 1);
	for (int i = 0; i < n; i ++ ){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
		deg[u] ++ ;
		deg[v] ++ ;
	}
	dsu d(n);
	vector <bool> used(n + 1, false);
	auto bfs = [&](){
		queue <int> q;
		for (int i = 1; i <= n; i ++ ){
			if (deg[i] == 1){
				q.push(i);
			}
		}
		while(q.size()){
			int u = q.front();
			q.pop();
			used[u] = true;
			for (auto v : e[u]){
				if (used[v]) continue;
				d.unite(u, v);
				deg[v] -- ;
				if (deg[v] == 1){
					q.push(v);
				}
			}
		}
	};
	bfs();
	int q;
	cin >> q;
	for (int i = 0; i < q; i ++ ){
		int u, v;
		cin >> u >> v;
		if (d.get(u) == d.get(v)) cout << "Yes\n";
		else cout << "No\n";
	}
	return 0;
}
posted on 2022-08-30 00:11  Hamine  阅读(42)  评论(0编辑  收藏  举报