【AtCoder】ExaWizards 2019 题解

【比赛链接】

【题解链接】

**【A】**Regular Triangle

【思路要点】

  • 判断是否 A = B = C A=B=C A=B=C
  • 时间复杂度 O ( 1 ) O(1) O(1)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
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 x, y, z;
	read(x), read(y), read(z);
	if (x == y && x == z) puts("Yes");
	else puts("No");
	return 0;
}

**【B】**Red or Blue

【思路要点】

  • 按题意模拟即可。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
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];
int main() {
	int n, r = 0, b = 0; read(n);
	scanf("\n%s", s + 1);
	for (int i = 1; i <= n; i++)
		if (s[i] == 'R') r++;
		else b++;
	if (r > b) puts("Yes");
	else puts("No");
	return 0;
}

**【C】**Snuke the Wizard

【思路要点】

  • 可以发现,若某处的傀儡走到了 1 1 1 号点的左侧,那么初始时在其左侧的傀儡都会走到 1 1 1 号点的左侧;若某处的傀儡走到了 N N N 号点的右侧,那么初始时在其右侧的傀儡都会走到 N N N 号点的右侧。
  • 因此,我们就可以通过二分找到最右侧的走到 1 1 1 号点的左侧的傀儡和最左侧的走到 N N N 号点的右侧的傀儡。
  • 时间复杂度 O ( Q L o g N ) O(QLogN) O(QLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
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 n, q;
char s[MAXN], c[MAXN], opt[MAXN];
bool gol(int pos) {
	for (int i = 1; i <= q; i++) {
		if (c[i] == s[pos]) {
			if (opt[i] == 'L') pos--;
			else pos++;
		}
		if (pos > n) return false;
		if (pos < 1) return true;
	}
	return false;
}
bool gor(int pos) {
	for (int i = 1; i <= q; i++) {
		if (c[i] == s[pos]) {
			if (opt[i] == 'L') pos--;
			else pos++;
		}
		if (pos > n) return true;
		if (pos < 1) return false;
	}
	return false;
}
int getl() {
	int l = 0, r = n;
	while (l < r) {
		int mid = (l + r + 1) / 2;
		if (gol(mid)) l = mid;
		else r = mid - 1;
	}
	return l;
}
int getr() {
	int l = 1, r = n + 1;
	while (l < r) {
		int mid = (l + r) / 2;
		if (gor(mid)) r = mid;
		else l = mid + 1;
	}
	return n + 1 - l;
}
int main() {
	read(n), read(q);
	scanf("\n%s", s + 1);
	for (int i = 1; i <= q; i++)
		scanf("\n%c %c", &c[i], &opt[i]);
	writeln(n - getl() - getr());
	return 0;
}

**【D】**Modulo Operations

【思路要点】

  • 考虑一个给定的取模序列 a 1 , a 2 , a 3 , . . . , a N a_1,a_2,a_3,...,a_N a1,a2,a3,...,aN ,其中可能产生作用的是作为前缀最小值出现的 a i a_i ai ,在其余的 a i a_i ai 处,当前数值已经不足 a i a_i ai ,可以视作没有进行取模。
  • 考虑将数组从大到小排序,对于可能产生作用的 a i a_i ai 进行动态规划。
  • f i , j f_{i,j} fi,j 表示考虑前 i i i 大的模数,得数为 j j j 的排列方式数,则有转移
    f i + 1 , j % a i + 1 ⇐ f i , j f_{i+1,j\%a_{i+1}}\Leftarrow f_{i,j} fi+1,j%ai+1fi,j
    f i + 1 , j ⇐ f i , j × ( N − ( i + 1 ) ) f_{i+1,j}\Leftarrow f_{i,j}\times(N-(i+1)) fi+1,jfi,j×(N(i+1))
  • 其中转移 1 1 1 代表认为 a i a_i ai 可能产生作用,转移 2 2 2 代表将 a i a_i ai 任意排列在后面的某个位置,不对数值产生影响。
  • 时间复杂度 O ( N × X ) O(N\times X) O(N×X)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 205;
const int MAXM = 1e5 + 5;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
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 n, x, a[MAXN], dp[MAXN][MAXM];
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int main() {
	read(n), read(x), dp[0][x] = 1;
	for (int i = 1; i <= n; i++)
		read(a[i]);
	sort(a + 1, a + n + 1, [&] (int x, int y) {return x > y; });
	for (int i = 1; i <= n; i++)
	for (int j = 0; j <= x; j++) {
		int tmp = dp[i - 1][j];
		update(dp[i][j % a[i]], tmp);
		update(dp[i][j], 1ll * tmp * (n - i) % P);
	}
	int ans = 0;
	for (int i = 0; i <= x; i++)
		update(ans, 1ll * dp[n][i] * i % P);
	writeln(ans);
	return 0;
}

**【E】**Black or White

【思路要点】

  • 分两种情况计算答案:
  • ( 1 ) (1) (1) 、在吃第 i i i 块之前,已经没有白巧克力剩余。
  • 此时第 i i i 块一定是黑巧克力,记 n w i nw_{i} nwi 表示吃完第 i i i 块后没有白巧克力剩余的概率,情况 ( 1 ) (1) (1) 对答案的贡献应当为 n w i − 1 nw_{i-1} nwi1
  • 计算 n w i nw_{i} nwi n w i = n w i − 1 + ( i − 1 W − 1 ) 2 i nw_{i}=nw_{i-1}+\frac{\binom{i-1}{W-1}}{2^i} nwi=nwi1+2i(W1i1)
  • ( 2 ) (2) (2) 、在吃第 i i i 块之前,两种巧克力均有剩余。
  • 此时每一种使得两种巧克力均有剩余的情况是等概率发生的,考虑计算 l f t i lft_i lfti 表示吃完第 i i i 块后两种巧克力均有剩余的情况总数,情况 ( 2 ) (2) (2) 对答案的贡献应当为 l f t i − 1 2 i \frac{lft_{i-1}}{2^i} 2ilfti1
  • 计算 l f t i lft_{i} lfti l f t i = 2 l f t i − 1 − ( i − 1 B − 1 ) − ( i − 1 M − 1 ) lft_{i}=2lft_{i-1}-\binom{i-1}{B-1}-\binom{i-1}{M-1} lfti=2lfti1(B1i1)(M1i1)
  • 时间复杂度 O ( B + W ) O(B+W) O(B+W)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
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 fac[MAXN], inv[MAXN], two[MAXN], itwo[MAXN];
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
int getc(int x, int y) {
	if (y > x) return 0;
	else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P;
}
void init(int n) {
	fac[0] = two[0] = itwo[0] = 1;
	for (int i = 1; i <= n; i++) {
		fac[i] = 1ll * fac[i - 1] * i % P;
		two[i] = 2ll * two[i - 1] % P;
		itwo[i] = (P + 1ll) / 2 * itwo[i - 1] % P;
	}
	inv[n] = power(fac[n], P - 2);
	for (int i = n - 1; i >= 0; i--)
		inv[i] = inv[i + 1] * (i + 1ll) % P;
}
int n, m, nw[MAXN], lft[MAXN];
//nw : P(No White Left)
//lft : Cnt(Both Left)
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int main() {
	read(n), read(m);
	init(n + m), lft[0] = 1;
	for (int i = 1; i <= n + m; i++) {
		nw[i] = nw[i - 1], lft[i] = 2ll * lft[i - 1] % P;
		update(nw[i], 1ll * itwo[i] * getc(i - 1, m - 1) % P);
		if (i >= n) update(lft[i], P - getc(i - 1, n - 1));
		if (i >= m) update(lft[i], P - getc(i - 1, m - 1));
		writeln((nw[i - 1] + 1ll * itwo[i] * lft[i - 1]) % P);
	}
	return 0;
}

**【F】**More Realistic Manhattan Distance

【思路要点】

  • 通过合适的预处理,我们可以 O ( 1 ) O(1) O(1) 回答距离某一行或列向某一方向上最近的某一朝向的路所在的位置。
  • 将起点和终点上方和下方第一个 E E E 方向的行和第一个 W W W 方向的行,以及起点和终点左方和右方第一个 S S S 方向的列和第一个 N N N 方向的列选出,在形成的交叉点上直接运行最短路算法即可。
  • 我们至多会选出 6 6 6 6 6 6 列,因此时间复杂度为 O ( N + K L o g K × Q ) O(N+KLogK\times Q) O(N+KLogK×Q) ,其中 K = 36 K=36 K=36

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
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("");
}
namespace ShortestPath {
	const ll INF = 1e18;
	const int MAXP = 1e6;
	struct edge {int dest, len; };
	int n; ll dist[MAXP];
	vector <edge> a[MAXP];
	set <pair <ll, int> > st;
	void addedge(int x, int y, int z) {
		a[x].push_back((edge) {y, z});
	}
	void init(int x) {
		n = x; st.clear();
		for (int i = 1; i <= n; i++) {
			dist[i] = INF;
			a[i].clear();
		}
	}
	void work(int s) {
		dist[s] = 0;
		st.insert(make_pair(0, s));
		while (!st.empty()) {
			pair <ll, int> tmp = *st.begin();
			st.erase(tmp);
			for (unsigned i = 0; i < a[tmp.second].size(); i++) {
				int dest = a[tmp.second][i].dest;
				ll newlen = tmp.first + a[tmp.second][i].len;
				if (newlen < dist[dest]) {
					st.erase(make_pair(dist[dest], dest));
					dist[dest] = newlen;
					st.insert(make_pair(dist[dest], dest));
				}
			}
		}
	}
}
int n, m, q;
char h[MAXN], v[MAXN];
int hpre[MAXN][2], hsuf[MAXN][2];
int vpre[MAXN][2], vsuf[MAXN][2];
int nx, ny; vector <int> x, y;
bool sx[MAXN], sy[MAXN];
void addx(int pos) {
	if (!sx[pos]) {
		sx[pos] = true;
		x.push_back(pos);
	}
}
void addy(int pos) {
	if (!sy[pos]) {
		sy[pos] = true;
		y.push_back(pos);
	}
}
int path(pair <int, int> s, pair <int, int> t) {
	nx = x.size(), ny = y.size();
	static int point[16][16];
	int tot = 0;
	for (int i = 0; i < nx; i++)
	for (int j = 0; j < ny; j++)
		point[i][j] = ++tot;
	ShortestPath :: init(tot);
	for (int i = 0; i < nx; i++)
	for (int j = 0; j < ny; j++) {
		if (h[x[i]] == 'E' && j < ny - 1) ShortestPath :: addedge(point[i][j], point[i][j + 1], y[j + 1] - y[j]);
		if (h[x[i]] == 'W' && j > 0) ShortestPath :: addedge(point[i][j], point[i][j - 1], y[j] - y[j - 1]);
		if (v[y[j]] == 'S' && i < nx - 1) ShortestPath :: addedge(point[i][j], point[i + 1][j], x[i + 1] - x[i]);
		if (v[y[j]] == 'N' && i > 0) ShortestPath :: addedge(point[i][j], point[i - 1][j], x[i] - x[i - 1]);
	}
	for (int i = 0; i < nx; i++)
	for (int j = 0; j < ny; j++)
		if (x[i] == s.first && y[j] == s.second) ShortestPath :: work(point[i][j]);
	for (int i = 0; i < nx; i++)
	for (int j = 0; j < ny; j++)
		if (x[i] == t.first && y[j] == t.second) {
			ll tmp = ShortestPath :: dist[point[i][j]];
			if (tmp > 1e8) return -1;
			else return tmp;
		}
	assert(false);
	return -1;
}
int main() {
	read(n), read(m), read(q);
	scanf("\n%s\n%s", h + 1, v + 1);
	for (int i = 1; i <= n; i++) {
		hpre[i][0] = hpre[i - 1][0];
		hpre[i][1] = hpre[i - 1][1];
		if (h[i] == 'E') hpre[i][1] = i;
		else hpre[i][0] = i;
	}
	for (int i = n; i >= 1; i--) {
		hsuf[i][0] = hsuf[i + 1][0];
		hsuf[i][1] = hsuf[i + 1][1];
		if (h[i] == 'E') hsuf[i][1] = i;
		else hsuf[i][0] = i;
	}
	for (int i = 1; i <= m; i++) {
		vpre[i][0] = vpre[i - 1][0];
		vpre[i][1] = vpre[i - 1][1];
		if (v[i] == 'S') vpre[i][1] = i;
		else vpre[i][0] = i;
	}
	for (int i = m; i >= 1; i--) {
		vsuf[i][0] = vsuf[i + 1][0];
		vsuf[i][1] = vsuf[i + 1][1];
		if (v[i] == 'S') vsuf[i][1] = i;
		else vsuf[i][0] = i;
	}
	sx[0] = sy[0] = true;
	for (int i = 1; i <= q; i++) {
		pair <int, int> s, t;
		read(s.first), read(s.second);
		read(t.first), read(t.second);
			addy(vsuf[s.second][0]);
			addy(vsuf[s.second][1]);
			addy(vpre[s.second][0]);
			addy(vpre[s.second][1]);
			addy(vpre[t.second][0]);
			addy(vpre[t.second][1]);
			addy(vsuf[t.second][0]);
			addy(vsuf[t.second][1]);
			addx(hsuf[s.first][0]);
			addx(hsuf[s.first][1]);
			addx(hpre[s.first][0]);
			addx(hpre[s.first][1]);
			addx(hpre[t.first][0]);
			addx(hpre[t.first][1]);
			addx(hsuf[t.first][0]);
			addx(hsuf[t.first][1]);
		sort(x.begin(), x.end());
		sort(y.begin(), y.end());
		writeln(path(s, t));
		for (auto pos : x)
			sx[pos] = false;
		for (auto pos : y)
			sy[pos] = false;
		x.clear(), y.clear();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值