2021牛客暑期多校訓練營2

比賽地址c++

C(水題)

題目連接
spa

題目:
給定一個\(n\times m\)的點陣,ZYT和LBC輪流走,ZYT先走,每一個人每次只能向上下左右四個方向走一次,且不能去到已經走過的點,ZYT先走,沒法走動的人輸,問這局遊戲的勝者是誰code

解析:
因爲不能走到已經去過的點,因此這張圖裏沒有環,因此是一顆生成樹,那麼只要判斷\(n*m\)的奇偶性便可blog

#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(阿波羅尼奧斯圓)

題目連接
⭐⭐get

題目:
給定一個三維座標系中的四個固定點\(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\)可移動範圍的交集的空間體積是多少it

解析:
根據\(P_1,P_2\)的範圍條件限制,不難看出他的活動範圍在一個阿波羅尼奧斯圓內部,這樣只要套一個圓相交求相交體積的板子便可(下給出推導過程,以\(P_1(x_0,y_0,z_0)\)爲例,注意\(1-k_1^2<0\)
io

#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");
}
相關文章
相關標籤/搜索