1. 程式人生 > 其它 >AtCoder Beginner Contest 246題解

AtCoder Beginner Contest 246題解

A - Four Points

題目描述:給你一個矩形的三個頂點座標,問第四個頂點的座標。

思路:根據題意模擬即可。

時間複雜度:\(O(1)\)

參考程式碼:

void solve() {
	int x, y, resx = 0, resy = 0;
	for (int i = 1; i <= 3; ++i) {
		cin >> x >> y;
		resx ^= x; resy ^= y;
	}
	cout << resx << " " << resy << '\n';
	return;
}

B - Get Closer

題目描述:告訴你一條經過原點的直線上的一個點,求這條直線上距離原點長度為\(1\)的點的座標。

思路:基礎的計算幾何,若\(x = 0\),直接輸出0 1,否則先求出斜率\(k\),然後求解:

\[x^2 + (kx)^2 = 1\\ y = kx \]

即可。

時間複雜度:\(O(1)\)

參考程式碼:

void solve() {
	int x, y;
	cin >> x >> y;
	if (x == 0) cout << "0 1" << '\n';
	else {
		double k = y * 1.0 / x;
		double resx = sqrt(1.0 / (1 + k * k));
		double resy = resx * k;
		cout << setprecision(10) << resx << " " << resy << '\n';
	}
	return;
}

C - Coupon

題目描述:你需要買\(n\)種物品,每種物品的價格為\(a_i\),給你\(k\)張優惠卷,優惠卷可以疊加,問你買這\(n\)種物品的最小花費。

思路:比較明顯的貪心,使用優先佇列維護一下當前物品價格的最大值即可。

時間複雜度:\(O(nlogn)\)

參考程式碼:

void solve() {
	int n, k, x;
	priority_queue<int> heap;
	cin >> n >> k >> x;
	for (int i = 1; i <= n; ++i) {
		int val;
		cin >> val;
		heap.push(val);
	}
	while (k != 0 && !heap.empty()) {
		auto price = heap.top();
		heap.pop();
		if (price <= x) --k;
		else {
			int need = price / x;
			if (need <= k) k -= need;
			else need = k, k = 0;
			price -= need * x;
			if (price != 0) heap.push(price);
		}
	}
	long long res = 0;
	while (!heap.empty()) {
		res += heap.top();
		heap.pop();
	}
	cout << res << '\n';
	return;
}

D - 2-variable Function

題目描述:對於方程\(x = a^3 +a^2b +ab^2 + b^3\),給定一個整數\(n\),求大於等於\(n\)的最小正整數\(x\),使得存在非負整數對\((a , b)\),使得上述等式成立。

思路:顯然我們可以列舉\(a\),其範圍為\(0 \leq a \leq 1e6\),此時\(a\)可以當做常數看待,原等式可以改寫成以下不等式:

\[b^3 + ab^2 + a^2b \geq x - a^3 \]

不等式左邊是非負的,所以在正整數範圍內以\(b\)為引數的函式是單調遞增的,所以可以二分去查詢一個最小的\(b\)使得該不等式成立。

時間複雜度:\(O(10^6log10^6)\)

參考程式碼:

void solve() {
	long long x;
	cin >> x;
	long long res = LLONG_MAX;
	for (int i = 0; i <= 1e6; ++i) {
		long long a = i;
		long long y = x - a * a * a;
		int lr = 0, rs = 1e6, ans = 0;
		while (lr <= rs) {
			long long mid = lr + rs >> 1;
			long long cur = mid * mid * mid + a * mid * mid + a * a * mid;
			if (cur >= y) ans = mid, rs = mid - 1;
			else lr = mid + 1;
		}
		long long cur = a * a * a + 1ll * ans * ans * ans + a * a * ans + a * ans * ans;
		res = min(res, cur);
	}
	cout << res << '\n';
	return;
}

E - Bishop 2

題目描述:你初始時在\((sx , sy)\)位置,你需要到\((ex , ey)\)位置,網格上有障礙物,你只能走斜對角線,每次行走,只要沒有障礙物,你可以走任意遠。問從起點到終點的最小次數。

思路:比較明顯的BFS,但因為一步可以走任意遠,所以噹噹前列舉的點的步數大於列舉的下一個點的步數時提前跳出,進行優化。

時間複雜度:\(O(能過)\)

參考程式碼:

void solve() {
	int n;
	int sx, sy, ex, ey;
	cin >> n >> sx >> sy >> ex >> ey;
	vector<string>strs(n + 1);
	for (int i = 1; i <= n; ++i) {
		cin >> strs[i];
		strs[i] = ' ' + strs[i];
	}
	vector<vector<int>>dis(n + 1, vector<int>(n + 1, 0x3f3f3f3f));
	dis[sx][sy] = 0;
	using PII = pair<int, int>;
	queue<PII> q;
	q.push({ sx , sy });
	while (!q.empty()) {
		auto [x, y] = q.front();
		q.pop();
		int dist = dis[x][y] + 1;
		for (int d = 1; d <= n; ++d) {
			int nx = x - d, ny = y - d;
			if (nx < 1 || ny < 1 || strs[nx][ny] == '#') break;
			if (dis[nx][ny] < dist) break;
			dis[nx][ny] = dist;
			q.push({ nx , ny });
		}
		for (int d = 1; d <= n; ++d) {
			int nx = x + d, ny = y + d;
			if (nx > n || ny > n || strs[nx][ny] == '#') break;
			if (dis[nx][ny] < dist) break;
			dis[nx][ny] = dist;
			q.push({ nx , ny });
		}
		for (int d = 1; d <= n; ++d) {
			int nx = x + d, ny = y - d;
			if (nx > n || ny < 1 || strs[nx][ny] == '#') break;
			if (dis[nx][ny] < dist) break;
			dis[nx][ny] = dist;
			q.push({ nx , ny });
		}
		for (int d = 1; d <= n; ++d) {
			int nx = x - d, ny = y + d;
			if (nx < 1 || ny > n || strs[nx][ny] == '#') break;
			if (dis[nx][ny] < dist) break;
			dis[nx][ny] = dist;
			q.push({ nx , ny });
		}
	}
	if (dis[ex][ey] == 0x3f3f3f3f) dis[ex][ey] = -1;
	cout << dis[ex][ey] << '\n';
	return;
}

F - typewriter

題目描述:給你\(n\)個鍵盤, 每個鍵盤可以列印的字元由一個字串給出,現在列印長度為\(L\)的字串,每次可以選擇一個鍵盤列印, 問共可以打印出多少不同的字串。

思路:考慮容斥,以\(n = 3\)為例,最終答案為:只使用第一個鍵盤 + 只使用第二個鍵盤 + 只使用第三個鍵盤 - 使用第一個和第二個鍵盤 - 使用第一個和第三個鍵盤 - 使用第二個和第三個鍵盤 + 使用所有鍵盤。考慮到\(n\)很小,可以暴力列舉使用的鍵盤的數量,然後根據使用數量的奇偶來判斷是加還是減,每種情況對答案的貢獻為使用的鍵盤的字符集的交集的模的\(L\)次冪,即假設使用的\(k\)個鍵盤都含有的字元的種數為\(m\),則對答案的貢獻是\(m^L\)

時間複雜度:\(O(2^n logL)\)

參考程式碼:

const int mod = 998244353;

void solve() {
	int n, L;
	cin >> n >> L;
	vector<int>typewriter;
	string s;
	for (int i = 0; i < n; ++i) {
		cin >> s;
		int x = 0;
		for (auto&& c : s) x |= (1 << (c - 'a'));
		typewriter.push_back(x);
	}
	auto quickPow = [&](int a, int p)->int {
		int ans = 1;
		while (p) {
			if (p & 1) ans = 1ll * ans * a % mod;
			p >>= 1;
			a = 1ll * a * a % mod;
		}
		return ans;
	};
	auto cal = [](int x)->int {
		int cnt = 0;
		for (int i = 30; i >= 0; --i) cnt += (x >> i) & 1;
		return cnt;
	};
	int res = 0;
	for (int i = 1; i < 1 << n; ++i) {
		int ch = (1 << 26) - 1;
		for (int j = 0; j < n; ++j) {
			if (i & (1 << j)) ch &= typewriter[j];
		}
		int dx = cal(ch), dy = cal(i);
		if (dy & 1) res = (res + quickPow(dx, L)) % mod;
		else res = ((res - quickPow(dx, L)) % mod + mod ) % mod;
	}
	cout << res << '\n';
	return;
}