2018 Benelux Algorithm Programming Contest (BAPC 18)


Contest Info


[Practice Link](https://codeforc.es/gym/102007)
Solved A B C D E F G H I J K
8/11 O O O - - O O - O O O
  • O 在比賽中通過
  • Ø 賽后通過
  • ! 嘗試了但是失敗了
  • - 沒有嘗試

Solutions


A A Prize No One Can Win

簽到。

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

const int N = 1e5 + 10;
int n, a[N], x;

int main() {
	while (scanf("%d%d", &n, &x) != EOF) {
		for (int i = 1; i <= n; ++i) scanf("%d", a + i);
		sort(a + 1, a + 1 + n);
		int res = 1;
		for (int i = 2; i <= n; ++i) {
			if (a[i] + a[i - 1] <= x) {
				++res;
			} else break;
		}
		printf("%d\n", res);
	}
	return 0;
}

B Birthday Boy

題意:
給出\(n\)個生日,現在要找個日期,使得在它前面離它最近的生日和它相距的天數最大,如果有多個,輸出和\(10-27\)相距天數最大的。

思路:
題意讀清楚就可以了,枚舉每一天。

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

using pII = pair <int, int>;
#define fi first
#define se second
int n; pII a[110]; char s[110];
int mon[] = {
	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
bool operator < (pII a, pII b) {
	if (a.fi < b.fi || (a.fi == b.fi && a.se <= b.se)) {
		return true;
	}
	return false;
}
int dis(pII a, pII b) {
	if (a < b) {
		if (a.fi == b.fi) return b.se - a.se;
		int res = mon[a.fi] - a.se;
		for (int i = a.fi + 1; i < b.fi; ++i) res += mon[i];
		res += b.se;
		return res;
	} else {
		int res = mon[a.fi] - a.se; 
		for (int i = a.fi + 1; i <= 12; ++i) res += mon[i];
		for (int i = 1; i < b.fi; ++i) res += mon[i];
		res += b.se; 
		return res;
	}
}

int main() {
	while (scanf("%d", &n) != EOF) {
		for (int i = 1; i <= n; ++i) scanf("%s %02d-%02d", s, &a[i].fi, &a[i].se);
		sort(a + 1, a + 1 + n, [&](pII x, pII y) {
			if (x.fi != y.fi) return x.fi < y.fi;
			return x.se < y.se;		
		});
		pII res = pII(-1, -1); int Max = -1;
		a[0] = a[n];
		int pos = 0;
		for (int i = 1; i <= 12; ++i) {
			for (int j = 1; j <= mon[i]; ++j) {
				pII t = pII(i, j);
				while (pos < n && a[pos + 1] < t) ++pos;
				if (dis(a[pos], t) > Max) {
					Max = dis(a[pos], t);
					res = t;
				} else if (dis(a[pos], t) == Max) {
					if (dis(pII(10, 28), t) < dis(pII(10, 28), res)) {
						res = t;
					}
				}
			}
		}
		printf("%02d-%02d\n", res.fi, res.se);
	}
	return 0;
}

C Cardboard Container

題意:
給出一個立方體的體積\(V\),算表面積

思路:
枚舉長寬高,長寬高是\(V\)的因子,枚舉量不大。

view code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void getfac(vector <int> &vec, int n) {
	vec.clear();
	for (int i = 1; 1ll * i * i <= n; ++i) {
		if (n % i == 0) {
			vec.push_back(i);
			if (i * i != n)
			vec.push_back(n / i);
		}
	}
}

int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		vector <int> vec_a;
		getfac(vec_a, n);
		ll res = 1e18;
		for (auto a : vec_a) {
			vector <int> vec_b;
			getfac(vec_b, n / a);
			for (auto &b : vec_b) {
				int c = n / a / b;
				res = min(res, 2ll * (a * b + a * c + b * c));
			}
		}
		printf("%lld\n", res);
	}
	return 0;
}

D Driver Disagreement

題意:
給出\(n\)個點的圖,每個點都有兩條邊,並且每個點有一個標記\(1\)或者\(0\)
現在兩個人在同一點,但是他們不知道自己在哪一點,一個人覺得他們在\(A\),另一個人覺得他們在\(B\)
然后他們所在的位置是\(A\)或者\(B\)
現在需要設計一種路線,使得沿着路線走,如果從\(A\)出發走到一個點和從\(B\)出發走到一個點那個點的標記不一樣,那么它們就知道誰對誰錯了。
現在問最少的路線長度。
這個路線是每次選擇往左走還是往右走,因為每個點有兩條邊

E Entirely Unsorted Sequences

題意:
\(n\)個數,定義一個數是有序的當且僅當它左邊的數都小於等於它,它右邊的數都大於等於它。
現在問有多少種這些數的排列,使得所有數都不是有序的。

F Financial Planning

題意:
給出一些理財產品,對於每個理財產品,剛開始需要投入\(c_i\)的錢,之后每一天都會獲得\(p_i\)的錢。
過了\(x\)天,一款理財產品帶來的收益是\(xp_i - c_i\)
現在問,如果選擇理財產品,使得\(x\)天后的收益大於等於\(M\)
要滿足\(x\)最小

思路:
二分\(x\),那么每款理財產品的收益固定,貪心的取。

view code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m, p[N], c[N];
bool check(ll x) {
	ll res = 0;
	for (int i = 1; i <= n; ++i) {
		ll t = 1ll * p[i] * x - c[i];
		res += max(0ll, t);
		if (res >= m) return true;
	}
	return res >= m;
}

int main() {
	while (scanf("%d%d", &n, &m) != EOF) {
		for (int i = 1; i <= n; ++i) scanf("%d%d", p + i, c + i);
		ll l = 0, r = 2e9, res = 2e9;
		while (r - l >= 0) {
			ll mid = (l + r) >> 1;
			if (check(mid)) {
				r = mid - 1;
				res = mid;
			} else {
				l = mid + 1;
			}
		}
		printf("%lld\n", res);
	}
	return 0;
}

G Game Night

題意:
有一個長度為\(n\)的字符串環,里面只有'A', 'B', 'C'三種字符。
問最少要移動多少個字符,使得同類字符所在的位置的連續的。

思路:
枚舉最終形態,那么如果一個位置最終形態對應的位置不是本身,那么這個字符要動。
維護三個前綴和\(O(1)\)算貢獻。

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

const int N = 1e5 + 10;
int n, A[N], B[N], C[N]; char s[N];
int get(char c, int l, int r) {
	if (l > r) return 0;
	if (c == 'A') return (A[r] - A[l - 1]);
	else if (c == 'B') return (B[r] - B[l - 1]);
	return (C[r] - C[l - 1]);
}
int gao(string t) {
	int num[3];
	for (int i = 0; i < 3; ++i)
		num[i] = get(t[i], 1, n);
	int res = 0;
	for (int i = 0; i <= num[0]; ++i) {
		res = max(res, get(t[0], 1, i) + get(t[1], i + 1, i + num[1]) + get(t[2], i + num[1] + 1, i + num[1] + num[2]) + get(t[0], i + num[1] + num[2] + 1, n));
	}
	return n - res;
}

int main() {
	while (scanf("%d%s", &n, s + 1) != EOF) {
		memset(A, 0, sizeof A);
		memset(B, 0, sizeof B);
		memset(C, 0, sizeof C);
		for (int i = 1; i <= n; ++i) {
			A[i] = A[i - 1] + (s[i] == 'A');
			B[i] = B[i - 1] + (s[i] == 'B');
			C[i] = C[i - 1] + (s[i] == 'C');
		}
		int res = 1e9;
		string t = "ABC";
		do {
			res = min(res, gao(t));
		} while (next_permutation(t.begin(), t.end()));
		printf("%d\n", res);
	}
	return 0;
}

H Harry the Hamster

題意:
有一張\(n\)個點\(m\)條邊的有向圖,每條邊有邊權,一個人在\(S\)點,它的左腦不想睡覺,它的右腦想睡覺。
兩個腦袋輪流操作,每次操作在當前點選擇一條出邊往下走。
保證除了\(T\)點之外每個點都有出邊,並且\(T\)沒有出邊。
問兩個腦袋都最優操作,最終能否到\(T\),能的話就輸出到\(T\)的時間,不能的話就輸出'infinity'

I In Case of an Invasion, Please. . .

題意:
給出一張\(n\)個點\(m\)條邊的無向圖,每個點有\(p_i\)個人,有\(s(1 \leq s \leq 10)\)個保護區。
每個保護區有一個容量\(c_i\),保證\(\sum c_i \geq \sum p_i\).
現在所有人都要到保護區,令所有人到保護區的最大時間最小。

思路:
二分答案,那么對於每個點,我們可以知道在這個答案下,這個點上的人可以到達哪些保護區。
那么用一個\(s\)的二進制狀態表示它的可達狀態,發現最多只有\(2^s\)種狀態。
將同種狀態的點都縮起來,網絡流判斷是否流量等於\(\sum p_i\)即可。

view code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 2e5 + 10;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;

struct Dicnic {
	static const int M = 2e6 + 10;
	static const int N = 1e4 + 10;

	struct Edge {
		int to, nxt;
		ll flow;

		Edge() {}

		Edge(int to, int nxt, ll flow): to(to), nxt(nxt), flow(flow) {}
	}edge[M];

	int S, T;
	int head[N], tot;
	int dep[N];

	void Init() {
		memset(head, -1, sizeof head);
		tot = 0;
	}

	void set(int _S, int _T) {
		S = _S;
		T = _T;
	}

	void addedge(int u, int v, ll w, ll rw = 0) {
		edge[tot] = Edge(v, head[u], w); head[u] = tot++;
		edge[tot] = Edge(u, head[v], rw); head[v] = tot++;
	}

	bool BFS() {
		memset(dep, -1, sizeof dep);
		queue<int>q;
		q.push(S);
		dep[S] = 1;
		while (!q.empty()) {
			int u = q.front();
			q.pop();
			for (int i = head[u]; ~i; i = edge[i].nxt) {
				if (edge[i].flow && dep[edge[i].to] == -1) {
					dep[edge[i].to] = dep[u] + 1;
					q.push(edge[i].to);
				}
			}
		}
		return dep[T] >= 0;
	}

	ll DFS(int u, ll f) {
		if (u == T || f == 0) {
			return f;
		}
		ll w, used = 0;
		for (int i = head[u]; ~i; i = edge[i].nxt) {
			if (edge[i].flow && dep[edge[i].to] == dep[u] + 1) {
				w = DFS(edge[i].to, min(f - used, edge[i].flow));
				edge[i].flow -= w;
				edge[i ^ 1].flow += w;
				used += w;
				if (used == f) return f;
			}
		}
		if (!used) dep[u] = -1;
		return used;
	}

	ll solve() {
		ll res = 0;
		while (BFS()) {
			res += DFS(S, INFLL);
		}
		return res;
	}
}dicnic;

struct Edge {
	int to, nxt;
	ll w;

	Edge() {}

	Edge(int to, int nxt, ll w): to(to), nxt(nxt), w(w){}
}edge[N << 2];

int n, m, s;
int a[N], c[N], p[N];
int head[N], used[N], tot;
ll dis[11][N];

void Init() {
	memset(head, -1, sizeof head);
	tot = 0;
}

void addedge(int u, int v, int w) {
	edge[tot] = Edge(v, head[u], w); head[u] = tot++;
	edge[tot] = Edge(u, head[v], w); head[v] = tot++;
}

struct qnode {
	int u;
	ll w;

	qnode() {}
	
	qnode(int u, ll w): u(u), w(w) {}

	bool operator < (const qnode &other) const {
		return w > other.w;
	}
};

void Dij(int id, int S) {
	for (int i = 1; i <= n; ++i) {
		dis[id][i] = INFLL;
		used[i] = false;
	}
	dis[id][S] = 0;
	priority_queue<qnode> pq;
	pq.push(qnode(S, 0));
	while (!pq.empty()) {
		int u = pq.top().u;
		pq.pop();
		if (used[u]) continue;
		used[u] = true;
		for (int i = head[u]; ~i; i = edge[i].nxt) {
			int v = edge[i].to, w = edge[i].w;
			if (!used[v] && dis[id][v] > dis[id][u] + w) {
				dis[id][v] = dis[id][u] + w;
				pq.push(qnode(v, dis[id][v]));
			}
		}
	}
}

ll sum;
ll mark[1 << 11];

bool check(ll x) {
	memset(mark, 0, sizeof mark);
	for (int i = 1; i <= n; ++i) {
		int S = 0;
		for (int j = 1; j <= s; ++j) {
			if (dis[j][i] <= x) {
				S |= 1 << (j - 1);
			}
		}
		mark[S] += p[i];
	}
	dicnic.Init();
	int S = (1 << s) + s + 10, T = S + 1, tmp = 1 << s;
	dicnic.set(S, T);
	for (int i = 0; i < tmp; ++i) {
		dicnic.addedge(S, i, mark[i]);
		for (int j = 0; j < s; ++j) {
			if (i & (1 << j)) {
				dicnic.addedge(i, j + tmp, mark[i]);
			}
		}
	}
	for (int i = (1 << s), j = 1; j <= s; ++j, ++i) {
		dicnic.addedge(i, T, c[j]);
	}
	ll res = dicnic.solve();
	return res == sum;
}

int main() {
	while (scanf("%d %d %d", &n, &m, &s) != EOF) {
		Init();
		sum = 0;
		for (int i = 1; i <= n; ++i) {
			scanf("%d", p + i);
			sum += p[i];
		}
		for (int i = 1, u, v, w; i <= m; ++i) {
			scanf("%d %d %d", &u, &v, &w);
			addedge(u, v, w);
		}
		for (int i = 1; i <= s; ++i) {
			scanf("%d %d", a + i, c + i);
		}
		for (int i = 1; i <= s; ++i) {
			Dij(i, a[i]);
		}
		ll l = 0, r = INFLL, res = INFLL;
		while (r - l >= 0) {
			ll mid = (l + r) >> 1;
			if (check(mid)) {
				r = mid - 1;
				res = mid;
			} else {
				l = mid + 1;
			}
		}
		printf("%lld\n", res);
	}
	return 0;
}

J Janitor Troubles

題意:
給出四條邊,問這四條邊構成的四邊形的最大面積

思路:
四邊形是內接圓四邊形時最大。

view code
#include <bits/stdc++.h>

using namespace std;

using db = double;

const db eps = 1e-8;

int sgn(db x) {
	if (fabs(x) < eps) return 0;
	else return x > 0 ? 1 : -1;
}

inline db F(db a, db b, db c) {
	db p = (a + b + c) / 2.0;
	db res = sqrt(p * (p - a) * (p - b) * (p - c));
	return res;
}

inline db f(db a, db b, db c, db d) {
	db res = 0.0;
	db l = max(fabs(a - b), fabs(c - d));
	db r = min(fabs(a + b), fabs(c + d));
	for (db e = l; e < r; e += 0.0005) {
		if (a + b - e <= 0) continue;
		if (a + e - b <= 0) continue;
		if (b + e - a <= 0) continue;

		if (c + d - e <= 0) continue;
		if (c + e - d <= 0) continue;
		if (d + e - a <= 0) continue;

		res = max(res, F(a, b, e) + F(c, d, e));
	}
	return res;
}

int a, b, c, d;

int main() {
	while (scanf("%d %d %d %d", &a, &b, &c, &d) != EOF) {
		if (a == b && b == c && c == d) {
			db res = a * b;
			printf("%.10f\n", res);
			continue;
		}
		db res = f(a, b, c, d);
		res = max(res, f(a, c, b, d));
		res = max(res, f(a, d, b, c));
		printf("%.15f\n", res);
	}
	return 0;
}

K Kingpin Escape

題意:
給出一棵樹,有一個特殊點,現在可以額外加一些邊,使得不管刪去原樹中的哪條邊,每個點仍然可以到達特殊點

view code
#include <bits/stdc++.h>

using namespace std;

int n, m;
vector<vector<int> >G;
vector<int> vec;

void gao(int u, int fa) {
	if (G[u].size() == 1) {
		vec.push_back(u);
		return ;
	}
	for (auto &v : G[u]) if (v != fa){
		gao(v, u);
	}
}

int main() {
	while (scanf("%d %d", &n, &m) != EOF) {
		G.clear();
		G.resize(n);
		vec.clear();
		for (int i = 1, u, v; i < n; ++i) {
			scanf("%d %d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		if (n == 2) {
			puts("1");
			puts("0 1");
			continue;
		}
		int rt = 0;
		for (int i = 0; i < n; ++i) {
			if (G[i].size() > 1) {
				rt = i;
				break;
			}
		}
		gao(rt, rt);
		int sze = vec.size();
		int cnt = (sze + 1) / 2;
		printf("%d\n", cnt);
		for (int i = 0; i + cnt < sze; ++i) {
			printf("%d %d\n", vec[i], vec[i + cnt]);
		}
		if (sze & 1) {
			if (vec[cnt - 1] != m) {
				printf("%d %d\n", vec[cnt - 1], m);
			} else {
				printf("%d %d\n", vec[cnt - 1], vec[cnt - 2]);
			}
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM