1. 程式人生 > >【CodeForces】CodeForces Round #460 (Div. 2) 題解

【CodeForces】CodeForces Round #460 (Div. 2) 題解

【比賽連結】

【題解連結】

【A】Supermarket

【思路要點】

  • 選取單價最低的店進行購買。
  • 時間複雜度\(O(N)\)。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int main() {
	int n, m; read(n), read(m);
	double ans = 1e99;
	for (int i = 1; i <= n; i++) {
		double x, y;
		read(x), read(y);
		ans = min(ans, m * x / y);
	}
	printf("%.10lf\n", ans);
	return 0;
}

【B】Perfect Number

【思路要點】

  • 列舉答案,暴力判斷是否為Perfect Number即可。
  • 時間複雜度\(O(AnsLogAns))\)。
  • 本題可以採用列舉位數和第一個數字的方式做到更優秀的複雜度。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int main() {
	int n, ans = 0; read(n);
	while (n != 0) {
		ans++;
		int tmp = ans, sum = 0;
		while (tmp) {
			sum += tmp % 10;
			tmp /= 10;
		}
		if (sum == 10) n--;
	}
	writeln(ans);
	return 0;
}


【C】Seat Arrangements

【思路要點】

  • 對每一行與每一列分別掃描,計算出每一段極長的連續空位的長度,以此來計算答案即可。
  • 注意對\(k=1\)的情況單獨處理。
  • 時間複雜度\(O(N*M)\)。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
char a[MAXN][MAXN];
int main() {
	int n, m, k, ans = 0;
	read(n), read(m), read(k);
	for (int i = 1; i <= n; i++)
		scanf("\n%s", a[i] + 1);
	if (k == 1) {
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (a[i][j] == '.') ans++;
		writeln(ans);
		return 0;
	}
	for (int i = 1; i <= n; i++) {
		int now = 0;
		for (int j = 1; j <= m + 1; j++)
			if (a[i][j] == '.') now++;
			else {
				if (now >= k) ans += now - k + 1;
				now = 0;
			}
	}
	for (int j = 1; j <= m; j++) {
		int now = 0;
		for (int i = 1; i <= n + 1; i++)
			if (a[i][j] == '.') now++;
			else {
				if (now >= k) ans += now - k + 1;
				now = 0;
			}
	}
	writeln(ans);
	return 0;
}


【D】Substring

【思路要點】

  • 首先,當且僅當圖中存在環時,答案為-1,因此首先DFS判斷是否有環。
  • 由於問題是求最大值的最大值,因此我們可以將每一個字母分開計算,取最大值。
  • 有向無環圖上的最長路徑我們可以用一個簡單DP在\(O(N+M)\)的時間內求解。
  • 因此,總時間複雜度為\(O(|S|*(N+M))\),本題中\(|S|=26\)。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
char s[MAXN], now;
int ans[MAXN];
vector <int> a[MAXN];
bool vis[MAXN], ins[MAXN], halted;
void chkmax(int &x, int y) {
	x = max(x, y);
}
int get(int pos) {
	if (ans[pos] != -1) return ans[pos];
	int tans = 0;
	for (unsigned i = 0; i < a[pos].size(); i++)
		chkmax(tans, get(a[pos][i]));
	return ans[pos] = tans + (now == s[pos]);
}
void visit(int pos) {
	if (vis[pos]) return;
	if (ins[pos]) {
		halted = true;
		return;
	}
	ins[pos] = true;
	for (unsigned i = 0; i < a[pos].size(); i++)
		visit(a[pos][i]);
	ins[pos] = false;
	vis[pos] = true;
}
int main() {
	int n, m; read(n), read(m);
	scanf("\n%s", s + 1);
	for (int i = 1; i <= m; i++) {
		int x, y;
		read(x), read(y);
		a[x].push_back(y);
	}
	for (int i = 1; i <= n; i++)
		visit(i);
	if (halted) {
		printf("-1\n");
		return 0;
	}
	int Max = 0;
	for (now = 'a'; now <= 'z'; now++) {
		memset(ans, -1, sizeof(ans));
		for (int i = 1; i <= n; i++)
			chkmax(Max, get(i));
	}
	writeln(Max);
	return 0;
}

【E】Congruence Equation

【思路要點】

  • 首先,\(P\)是質數,所以,\(a^{P-1}\equiv 1(Mod\ P)(a=1,2,...,P-1)\)。

  • \(n*a^{n}\equiv b(Mod P)\Leftrightarrow n\equiv b*a^{-n}(Mod\ P)\)。
  • 等式的左側是一個週期為\(P\)的數列,而等式的右側是一個週期為\(P-1\)的數列。
  • 可以證明,二元組\((i\ Mod\ P,b*a^{-i}\ Mod\ P)(i\in N^{+})\)以\(P*(P-1)\)為一個週期,且一個週期內每一個二元組恰好出現一次。
  • 因此,我們列舉可能對答案產生貢獻的(即兩項相等的)\(P-1\)個二元組,計算其第一次出現時\(i\)的取值,並統計對答案的貢獻即可。
  • 時間複雜度\(O(P)\)。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
long long a, b, P, x;
long long inv[MAXN], val[MAXN];
int main() {
	read(a), read(b), read(P), read(x);
	inv[1] = 1;
	for (int i = 2; i <= P - 1; i++) {
		long long q = P / i, r = P % i;
		inv[i] = (P - q) * inv[r] % P;
	}
	val[0] = 1;
	for (int i = 1; i <= P - 1; i++)
		val[i] = val[i - 1] * a % P;
	for (int i = 1; i <= P - 1; i++)
		val[i] = b * inv[val[i]] % P;
	long long ans = 0;
	for (int i = 1; i <= P - 1; i++) {
		long long tmp = i + (P - 1) * ((i + P - val[i]) % P);
		if (tmp <= x) ans += 1 + (x - tmp) / (P * (P - 1));
	}
	writeln(ans);
	return 0;
}

【F】A Game With Numbers

【思路要點】

  • 每個玩家本質不同的手牌集合共有\(\binom{12}{4}=495\)種,因此本質不同的局面數共有\(2*495^{2}=490050\)種(算上玩家的先後手)。
  • 將遊戲圖構建出來(每個點出度不會超過16,因此遊戲圖的大小是可以接受的),並標明遊戲結束狀態的勝負性。
  • 反向儲存遊戲圖的邊,並且維護每個點在原圖中的出度。
  • 將已經確定勝負性的狀態放入佇列中,依次掃描。將在新圖中被一個必敗態的節點指向的點標為必勝態,若它還沒有被加入佇列,就將其加入佇列;將在新圖中被一個必勝態的節點指向的點在原圖中的出度減1,若出度減少到0,且它還沒有被加入佇列,就將其標為必敗態,並加入佇列。
  • 這個過程確定了所有必勝和必敗態節點的勝負性,其餘的節點則為平局態。
  • 時間複雜度\(O(M+T)\),其中\(M\)為遊戲圖的邊數。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXM = 1 << 25;
const int MAXN = 490055;
const int MAXS = 1 << 12;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct info {int cnt[5]; };
int hash(info a) {
	int ans = 0, now = -1;
	for (int i = 0; i <= 3; i++) {
		now += 1 + a.cnt[i];
		ans |= 1 << now;
	}
	return ans;
}
info decode(int x) {
	info ans;
	int now = 0, pos = 0;
	for (int i = 1, j = 1; i <= 12; i++, j <<= 1)
		if (x & j) ans.cnt[pos++] = now, now = 0;
		else now++;
	ans.cnt[pos] = now;
	return ans;
}
int cnt, num[MAXM];
int d[MAXN], bits[MAXS];
bool vis[MAXN], win[MAXN];
vector <int> a[MAXN];
int l, r, q[MAXN];
int main() {
	for (int i = 0; i < MAXS; i++) {
		int ans = 0, tmp = i;
		while (tmp) {
			ans += tmp & 1;
			tmp >>= 1;
		}
		bits[i] = ans;
	}
	for (int t = 0; t <= 1; t++)
	for (int i = 0; i < MAXS; i++) {
		if (bits[i] != 4) continue;
		for (int j = 0; j < MAXS; j++) {
			if (bits[j] != 4) continue;
			int S = (t << 24) + (i << 12) + j;
			num[S] = ++cnt;
		}
	}
	for (int t = 0; t <= 1; t++)
	for (int i = 0; i < MAXS; i++) {
		if (bits[i] != 4) continue;
		info fi = decode(i);
		for (int j = 0; j < MAXS; j++) {
			if (bits[j] != 4) continue;
			info fj = decode(j);
			int S = num[(t << 24) + (i << 12) + j];
			if (t == 0) {
				for (int ti = 1; ti <= 4; ti++) {
					if (fi.cnt[ti] == 0) continue;
					for (int tj = 1; tj <= 4; tj++) {
						if (fj.cnt[tj] == 0) continue;
						int res = (ti + tj) % 5;
						fi.cnt[ti]--, fi.cnt[res]++;
						int T = num[((t ^ 1) << 24) + (hash(fi) << 12) + j];
						d[S]++, a[T].push_back(S);
						fi.cnt[ti]++, fi.cnt[res]--;
					}
				}
			} else {
				for (int ti = 1; ti <= 4; ti++) {
					if (fi.cnt[ti] == 0) continue;
					for (int tj = 1; tj <= 4; tj++) {
						if (fj.cnt[tj] == 0) continue;
						int res = (ti + tj) % 5;
						fj.cnt[tj]--, fj.cnt[res]++;
						int T = num[((t ^ 1) << 24) + (i << 12) + hash(fj)];
						d[S]++, a[T].push_back(S);
						fj.cnt[tj]++, fj.cnt[res]--;
					}
				}
			}
		}
	}
	l = 0, r = -1;
	for (int t = 0; t <= 1; t++)
	for (int i = 0; i < MAXS; i++) {
		if (bits[i] != 4) continue;
		for (int j = 0; j < MAXS; j++) {
			if (bits[j] != 4) continue;
			int S = num[(t << 24) + (i << 12) + j];
			if (i == 3840) {
				vis[S] = true;
				win[S] = t ^ 1;
				q[++r] = S;
			} else if (j == 3840) {
				vis[S] = true;
				win[S] = t;
				q[++r] = S;
			}
		}
	}
	while (l <= r) {
		int tmp = q[l++];
		if (win[tmp]) {
			for (unsigned i = 0; i < a[tmp].size(); i++) {
				d[a[tmp][i]]--;
				if (!vis[a[tmp][i]] && d[a[tmp][i]] == 0) {
					vis[a[tmp][i]] = true;
					win[a[tmp][i]] = false;
					q[++r] = a[tmp][i];
				}
			}
		} else {
			for (unsigned i = 0; i < a[tmp].size(); i++) {
				if (!vis[a[tmp][i]]) {
					vis[a[tmp][i]] = true;
					win[a[tmp][i]] = true;
					q[++r] = a[tmp][i];
				}
			}
		}
	}
	int T; read(T);
	while (T--) {
		int s, tmp; read(s);
		tmp = s; info a, b;
		memset(a.cnt, 0, sizeof(a.cnt));
		memset(b.cnt, 0, sizeof(b.cnt));
		for (int i = 1; i <= 8; i++) {
			int x; read(x);
			a.cnt[x]++;
		}
		for (int i = 1; i <= 8; i++) {
			int x; read(x);
			b.cnt[x]++;
		}
		s <<= 12; s += hash(a);
		s <<= 12; s += hash(b);
		s = num[s];
		if (vis[s]) {
			if (win[s] ^ tmp) printf("Alice\n");
			else printf("Bob\n");
		} else printf("Deal\n");
	}
	return 0;
}