2021牛客暑期多校训练营2

时间:2021-08-13
本文章向大家介绍2021牛客暑期多校训练营2,主要包括2021牛客暑期多校训练营2使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

比赛地址

C(水题)

题目链接

题目:
给定一个\(n\times m\)的点阵,ZYT和LBC轮流走,ZYT先走,每个人每次只能向上下左右四个方向走一次,且不能去到已经走过的点,ZYT先走,无法走动的人输,问这局游戏的胜者是谁

解析:
由于不能走到已经去过的点,所以这张图里没有环,所以是一颗生成树,那么只要判断\(n*m\)的奇偶性即可

#include<bits/stdc++.h>
 
using namespace std;
 
int a, b;
 
 
int main() {
    scanf("%d%d", &a, &b);
    printf("%s", a * b & 1 ? "NO" : "YES");
}

D(水题)

题目链接

题目:
总共有4套牌,牌的大小为\(2\sim 10\),现在若有以下比较规则,对两副手牌\((a_1,b_1)(a_2,b_2)\)进行比较

  1. \((2,8)\)是最大的
  2. 不是\((2,8)\),则\(a=b\)是最大的
  3. 如果\(a_1=b_1\wedge a_2=b_2\),则取\(a_i\)中较大者
  4. 如果\(a_1\ne b_1\wedge a_2\ne b_2\),则取\((a_1+b_1)\mod 10,(a_2+b_2)\mod 10\)中较大者
  5. 如果\((a_1+b_1)\mod 10=(a_2+b_2)\mod 10\),则取\(b_i\)中较大者

解析:
根据题意暴力if判断

#include<cstdio>
#include<algorithm>

using namespace std;

int main() {
    int T;
    scanf("%d", &T);
    int a[2], b[2];
    while (T--) {
        scanf("%d%d%d%d", &a[0], &b[0], &a[1], &b[1]);
        if (a[0] > b[0]) swap(a[0], b[0]);
        if (a[1] > b[1]) swap(a[1], b[1]);
        if (a[0] == a[1] && b[0] == b[1])
            printf("tie");
        else if (a[0] == 2 && b[0] == 8)
            printf("first");
        else if (a[1] == 2 && b[1] == 8)
            printf("second");
        else if (a[0] == b[0]) {
            if (a[1] != b[1] || a[0] > a[1]) printf("first");
            else printf("second");
        } else if (a[1] == b[1])
            printf("second");
        else {
            int c[2] = {(a[0] + b[0]) % 10, (a[1] + b[1]) % 10};
            if (c[0] == c[1]) printf("%s", b[0] > b[1] ? "first" : "second");
            else printf("%s", c[0] > c[1] ? "first" : "second");
        }
        printf("\n");
    }
}

F(阿波罗尼奥斯圆)

题目链接
⭐⭐

题目:
给定一个三维坐标系中的四个固定点\(A,B,C,D\),有两个任意位置的点\(P_1,P_2\),需要满足\(|AP_1|\ge k_1|BP_1|,|CP_2|\ge k_2|DP_2|\),则\(P_1,P_2\)可移动范围的交集的空间体积是多少

解析:
根据\(P_1,P_2\)的范围条件限制,不难看出他的活动范围在一个阿波罗尼奥斯圆内部,这样只要套一个圆相交求相交体积的板子即可(下给出推导过程,以\(P_1(x_0,y_0,z_0)\)为例,注意\(1-k_1^2<0\)

#include<bits/stdc++.h>

using namespace std;

const double pi = acos(-1);
const double eps = 1e-9;
const int inf = 1e9 + 7;

typedef struct {
	double x, y, z, r;
}Point;
Point s[2];

//两点之间距离
double dis(Point p, Point q) {
	double ans = sqrt((p.x - q.x) * (p.x - q.x) + (p.y - q.y) * (p.y - q.y) + (p.z - q.z) * (p.z - q.z));
	return ans;
}

double calV(Point p) {
	return (4.0 / 3) * pi * (p.r * p.r * p.r);
}

double solve(Point a, Point s) {
	if (s.r < a.r)swap(a, s);
	double ans = 0;
	double d = dis(s, a);
	if (d >= a.r + s.r + eps)ans = 0;
	else if (d + a.r <= s.r)ans += calV(a);
	else if (d + s.r <= a.r)ans += calV(s);
	else if (fabs(s.r - a.r) <= d + eps && d <= a.r + s.r + eps) {
		double co = (s.r * s.r + d * d - a.r * a.r) / (2.0 * d * s.r);
		double h = s.r * (1 - co);
		ans += pi * h * h * (s.r - h / 3.0);
		co = (a.r * a.r + d * d - s.r * s.r) / (2.0 * d * a.r);
		h = a.r * (1 - co);
		ans += pi * h * h * (a.r - h / 3.0);
	}
	return ans;
}

double get_res() {
	double ans = 0;
	double d = dis(s[0], s[1]);
	if (d >= s[0].r + s[1].r) {
		return 0;
	}
	else if (d + s[1].r <= s[0].r) {
		ans += (4.0 / 3) * pi * s[1].r * s[1].r * s[1].r;
	}
	else {
		double co = (s[0].r * s[0].r + d * d - s[1].r * s[1].r) / (2.0 * d * s[0].r);
		double h = s[0].r * (1 - co);
		ans += (1.0 / 3) * pi * (3.0 * s[0].r - h) * h * h;
		co = (s[1].r * s[1].r + d * d - s[0].r * s[0].r) / (2.0 * d * s[1].r);
		h = s[1].r * (1 - co);
		ans += (1.0 / 3) * pi * (3.0 * s[1].r - h) * h * h;
	}
	return ans;
}

int main() {
	int T;
	double a[12];
	double k[2];
	scanf("%d", &T);
	while (T--) {
		memset(s, 0, sizeof(s));
		for (int i = 0; i < 12; ++i)
			scanf("%lf", a + i);
		for (int i = 0; i < 2; ++i)
			scanf("%lf", k + i);
		for (int i = 0; i < 2; ++i) {
			s[i].x = (k[i] * k[i] * a[6 * i + 3] - a[6 * i]) / (k[i] * k[i] - 1);
			s[i].y = (k[i] * k[i] * a[6 * i + 4] - a[6 * i + 1]) / (k[i] * k[i] - 1);
			s[i].z = (k[i] * k[i] * a[6 * i + 5] - a[6 * i + 2]) / (k[i] * k[i] - 1);
			for (int j = 0; j < 3; ++j)
				s[i].r += k[i] * k[i] * (a[6 * i + j] - a[6 * i + j + 3]) * (a[6 * i + j] - a[6 * i + j + 3]);
			s[i].r = sqrt(s[i].r / (k[i] * k[i] - 1) / (k[i] * k[i] - 1));
		}
		printf("%.3f\n", solve(s[0], s[1]));
	}
	return 0;
}

G(dp+贪心+优先队列)

题目链接
⭐⭐⭐⭐⭐

题目:
\(n\)个区间分成\(k\)组,每组取交集,求交集之和的最大值

解析:

  1. 贪心的考虑大区间(即存在给定区间是他的子区间),那么对于大区间最优策略只有两种:i 将大区间与其对应的某个子区间划分在一组(如果不与子区间划分在一起,则势必会与其他相交区间取交集,可能使得答案变小); ii 单独作为一组
  2. 那么对于剩下的小区间,考虑在按先左端点后右端点进行增序排序后,利用动态规划获得最优解。定义\(dp[i][j]\)为前\(j\)个区间分成\(i\)组所能获得的最大值
\[dp[i][j]=\max\{dp[i-1][k-1]+k.right-j.left\} \]

状态转移式即为从之前某个区间(包括自身)作为这个分组中的头,当前枚举区间作为分组的尾,每次首右端点,尾左端点做差即为这之间合为一组的贡献(由于左端点按增序排列,所以交集越来越小可以直接计算)再加上头之前的区间分成\(i-1\)组时的\(dp\)值,这个最大值可以用优先队列进行维护

补充:

  1. 如果不对大小区间进行分类,则\(dp\)转移式中一组内区间的不能保证均为相交(即有可能为子集关系),也就不能\(O(1)\)的计算分组贡献
  2. 必须对小区间进行排序,因为最优策略的情况下,大体去贪心地思考时,无疑区间一定是紧挨着的,这样可以省去区间比较的运算从而降维,另外这样也方便\(O(1)\)的计算分组贡献,
#include<bits/stdc++.h>

using namespace std;
typedef pair<int, int> pi;
const int maxn = 5e3 + 5;
pi a[maxn], small[maxn];
int big[maxn];
int sc, bc;

int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; ++i)
		scanf("%d%d", &a[i].first, &a[i].second);
	sort(a + 1, a + n + 1);
	int mn = 0x3f3f3f3f;
	for (int i = n; i; --i) {
		if (a[i].second < mn) {
			mn = a[i].second;
			small[sc++] = a[i];
		}
		else
			big[++bc] = a[i].second - a[i].first;
	}
	sort(big + 1, big + bc + 1, greater<int>());
	for (int i = 1; i <= bc; ++i)
		big[i] += big[i - 1];
	reverse(small, small + sc);
	vector<vector<int>> dp(2, vector<int>(sc + 1));
	dp[0].assign(n + 1, -0x3f3f3f3f);
	dp[0][0] = 0;
	deque<int> q;
	int ans = 0;
	for (int i = 1; i <= k; ++i) {
		dp[1].assign(n + 1, -0x3f3f3f3f);
		q.clear();
		for (int j = i - 1; j < sc; ++j) {
			while (!q.empty() && small[q.back()].second + dp[0][q.back()] <= small[j].second + dp[0][j]) q.pop_back();
			q.emplace_back(j);
			while (!q.empty() && small[q[0]].second <= small[j].first) q.pop_front();
			dp[1][j + 1] = dp[0][q[0]] + small[q[0]].second - small[j].first;
		}
		if (bc >= k - i)
			ans = max(ans, dp[1][sc] + big[k - i]);
		swap(dp[0], dp[1]);
	}
	printf("%d", ans);
}

I(模拟+BFS)

题目链接
⭐⭐⭐⭐

题目:
给出两张图并排放置,大小均为\(20\times 20\),其中有一些点上有阻碍物,现在两只企鹅分别要从\((20,20)\rightarrow(1,20),(20,1)\rightarrow(1,1)\),他们同时出发,接受相同指令,但移动方向满足下列要求

  1. L 左边的企鹅向左走,右边的企鹅向右走
  2. R 左边的企鹅向右走,右边的企鹅向左走
  3. U 都向上走
  4. D 都向下走

现在需要给出两只企鹅同时到达终点的最小距离以及距离

解析:
假如只有一张图,那很显然用BFS可以立刻求得结果,有两张图的时候,则考虑将两张图的位置组合起来看作是一张大图的某个点,这个点由一个四维坐标描述,前两维代表左边企鹅的坐标,后两维代表右边企鹅的坐标,跑一下BFS即可

#include<bits/stdc++.h>

using namespace std;
bool flag;

int dir1[4][2] = { 1,0,0,-1,0,1,-1,0 };
int dir2[4][2] = { 1,0, 0,1,0,-1,-1,0 };
char ch[4] = { 'U','R','L' ,'D' };

int bas = 20;

char mp1[20][25], mp2[20][25];
int path[405][405];
pair<int, int> tp[405][405];

pair<int, int> p;
queue<pair<int, int>> q;

int f(int x, int y) {
	return x * bas + y;
}

void get(int& x, int& y, int z) {
	x = z / bas, y = z % bas;
}

bool check(int x1, int y1) {
	return x1 >= 0 && x1 < 20 && y1 >= 0 && y1 < 20;
}

int cnt = 0;

void bfs() {
	path[f(bas - 1, bas - 1)][f(bas - 1, 0)] = -2;
	q.push({ f(bas - 1,bas - 1) ,f(bas - 1,0) });
	while (!q.empty()) {
		auto t = q.front(); q.pop();
		int x1, x2, y1, y2;
		get(x1, y1, t.first), get(x2, y2, t.second);
		for (int i = 0; i < 4; ++i) {
			int tx1 = x1 + dir1[i][0], ty1 = y1 + dir1[i][1];
			int tx2 = x2 + dir2[i][0], ty2 = y2 + dir2[i][1];
			if (mp1[tx1][ty1] == '#' || !check(tx1, ty1))
				tx1 = x1, ty1 = y1;
			if (mp2[tx2][ty2] == '#' || !check(tx2, ty2))
				tx2 = x2, ty2 = y2;
			if (path[f(tx1, ty1)][f(tx2, ty2)] == -1) {
				path[f(tx1, ty1)][f(tx2, ty2)] = 3 - i;
				tp[f(tx1, ty1)][f(tx2, ty2)] = t;
				if (tx1 == 0 && ty1 == bas - 1 && tx2 == 0 && ty2 == 0) return;
				q.push({ f(tx1, ty1) ,f(tx2, ty2) });
			}
		}
	}
}

void P(int x1, int y1, int x2, int y2, int i, int cnt) {
	mp1[x1][y1] = mp2[x2][y2] = 'A';
	int tx1, ty1, tx2, ty2;
	get(tx1, ty1, tp[f(x1, y1)][f(x2, y2)].first), get(tx2, ty2, tp[f(x1, y1)][f(x2, y2)].second);
	if (i != -2) {
		P(tx1, ty1, tx2, ty2, path[f(tx1, ty1)][f(tx2, ty2)], cnt + 1);
		printf("%c", ch[i]);
	}
	else
		printf("%d\n", cnt);
}

int main() {
	//freopen("abc.in", "r", stdin);
	memset(path, -1, sizeof(path));
	for (int i = 0; i < bas; ++i)
		scanf("%s %s", mp1[i], mp2[i]);
	bfs();
	P(0, bas - 1, 0, 0, path[f(0, bas - 1)][f(0, 0)], 0);
	for (int i = 0; i < bas; ++i)
		printf("\n%s %s", mp1[i], mp2[i]);
}

K(构造+拓扑排序)

题目链接
⭐⭐⭐

题目:
有一个非严格单调递增栈,有\(n\)个未知的数被压入单调栈内,现给出\(k\)个时刻栈大小,构造出一组合法压栈序列,无法构造则返回\(-1\)

解析:
由于是非严格单调递增栈,所以为了方便将站内元素弹出,尽可能的要保证压栈元素不同。那么不难得出假如加入将所给条件按照时刻排序,那么\(sz_i-sz_{i-1}\le t_i-t_{i-1}\)才有可能构造合法栈。
如果是一个严格单调递增栈,那么很容易想到构造栈满足栈内元素为\(1\sim k\)即可。但对于这道题来说,为了使得期望插入的值\(x\)从相同变为不相同时,则要满足前边出现的值要大于后边出现的值,这样才可以来维护原本的栈大小\(x\),那维护上述的拓扑关系,进行拓扑排序即可

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

typedef pair<int, int> pi;

const int maxn = 1e6 + 6;
pi sor[maxn], a[maxn];
int ans[maxn];

int n, k;

bool solve() {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= k; ++i)
		scanf("%d%d", &a[i].first, &a[i].second);
	sort(a + 1, a + k + 1);
	for (int i = 1; i <= k; ++i) {
		if (a[i].first - a[i - 1].first < a[i].second - a[i - 1].second) return false;
		for (int j = a[i - 1].first + 1; j < a[i].first; ++j)
			sor[j] = { j - a[i - 1].first + a[i - 1].second,j };
		sor[a[i].first] = { a[i].second,a[i].first };
	}
	for (int j = a[k].first + 1; j <= n; ++j)
		sor[j] = { j - a[k - 1].first + a[k - 1].second,j };
	sort(sor + 1, sor + 1 + n, [](pi a, pi b) {
		if (a.first != b.first)
			return a.first < b.first;
		return a.second > b.second;
		});
	for (int i = 1; i <= n; ++i)
		ans[sor[i].second] = i;
	return true;
}

int main() {
	if (solve())
		for (int i = 1; i <= n; ++i)
			printf("%d ", ans[i]);
	else
		printf("-1");
}
努力变成更好的自己把!

原文地址:https://www.cnblogs.com/DreamW1ngs/p/15139306.html