← 全部文章

AtCoder Grand Contest 044 题解

本文目录(6 节)
  1. Pay to Win
  2. Joker
  3. Strange Dance
  4. Guess the Password
  5. Random Pawn
  6. Name-Preserving Clubs

AtCoder Grand Contest 044

Pay to Win

倒着考虑,记忆化搜索一下即可。

#define ll __int128
const int o[3] = {2,3,5};
const ll inf = 1e18;
ll n, a[3], d;
map<ll, ll> p;

inline ll f(ll x) {
	if (!x) return 0;
	if (p.find(x) != p.end()) return p[x];
	p[x] = d * x;
	for (int i = 0; i < 3; i++) {
		int k = o[i];
		if (!(x % k)) p[x] = min(p[x], f(x / k) + a[i]);
		else {
			int t = x % k;
			p[x] = min(p[x], f(x / k) + a[i] + t * d);
			p[x] = min(p[x], f(x / k + 1) + a[i] + (k - t) * d);
		}
	}
	return p[x];
}

inline void solve() {
	rd(n), rd(a[0], a[1], a[2]), rd(d);
	p.clear();
	print(f(n));
}

int main() {
	int T;
	rd(T);
	while (T--) solve(); 
	return 0;
}

Joker

直接暴力 01BFS 求最短路即可,即每次去掉一个数之后松弛周围需要松弛的点。

这样复杂度看上去是 O(n4)\mathcal O(n^4) 的,但是仔细分析之后会发现,由于每个点最短路最长也是 O(n)\mathcal O(n) 的,而每次松弛必然会变短,因此每个点最多松弛 O(n)\mathcal O(n) 次,总时间复杂度为 O(n3)\mathcal O(n^3)。

const int N = 507;
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, 1, -1};
int n, a[N*N], d[N][N], ans;
bool v[N][N];

inline void F(int z, int &x, int &y) {
	x = (z - 1) / n + 1, y = (z - 1) % n + 1;
}

int main() {
	rd(n), rda(a, n * n);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			d[i][j] = min(min(i, n + 1 - i), min(j, n + 1 - j)), v[i][j] = 1;
	for (int i = 1, x, y; i <= n * n; i++) {
		F(a[i], x, y);
		ans += --d[x][y], v[x][y] = 0;
		deque<pi> q;
		q.pb(mp(x, y));
		while (q.size()) {
			x = q.front().fi, y = q.front().se, q.pop_front();
			for (int o = 0; o < 4; o++) {
				int X = x + dx[o], Y = y + dy[o];
				if (X < 1 || X > n || Y < 1 || Y > n) continue;
				if (d[X][Y] > d[x][y] + v[X][Y]) {
					d[X][Y] = d[x][y] + v[X][Y];
					if (v[X][Y]) q.pb(mp(X, Y));
					else q.push_front(mp(X, Y));
				}
			}
		}
	}
	print(ans);
	return 0;
}

Strange Dance

基本上就是 这题。

反向建 012Trie,S 操作就是交换所有点的 1,21,2 子树,打个 lazy tag 即可;R 操作就是抽出一条从根开始的全 22 链,将链上的 0→10 \to 1,1→21 \to 2,2→02 \to 0。

const int N = 6377292 + 7;
int n, m, trie[N][3], num[N], tot = 1, ans[N];
bool tag[N];
char s[N];

void build(int p, int d, int o, int x) {
	if (d == n) return num[p] = x, void();
	for (int i = 0; i < 3; i++)
		build(trie[p][i] = ++tot, d + 1, o * 3, x + i * o);
}

inline void spread(int p) {
	if (tag[p]) {
		swap(trie[p][1], trie[p][2]), tag[p] = 0;
		for (int i = 0; i < 3; i++) tag[trie[p][i]] ^= 1;
	}
}

void dfs(int p, int d, int o, int x) {
	if (d == n) return ans[num[p]] = x, void();
	spread(p);
	for (int i = 0; i < 3; i++)
		dfs(trie[p][i], d + 1, o * 3, x + i * o);
}

int main() {
	rd(n), build(1, 0, 1, 0), rds(s, m);
	for (int i = 1; i <= m; i++)
		if (s[i] == 'S') tag[1] ^= 1;
		else {
			int p = 1;
			for (int i = 0; i < n; i++, p = trie[p][0])
				spread(p),
				swap(trie[p][0], trie[p][1]),
				swap(trie[p][0], trie[p][2]);
		}
	dfs(1, 0, 1, 0);
	for (int i = 0, k = pow(3, n); i < k; i++)
		print(ans[i], " \n"[i==k-1]);
	return 0;
}

Guess the Password

交互题。

每次询问可以得到一个串与答案的最长公共子序列。

于是先搞出每个字符有多少个,然后把所有子序列归并起来即可。

const string str = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890";
int n;
vector<string> ss;

inline int ask(const string& s) {
	cout << "? " << s << endl;
	int x;
	cin >> x;
	return x;
}

string merge(int l, int r) {
	if (l == r) return ss[l];
	int mid = (l + r) >> 1;
	string sl = merge(l, mid), sr = merge(mid + 1, r), s;
	for (int i = 0, j = 0, k = n - sl.size(); i <= (int)sl.size(); i++) {
		if (i == (int)sl.size()) {
			while (j < (int)sr.size()) s += sr[j++];
			break;
		}
		while (j < (int)sr.size() && ask(s + sr[j] + sl.substr(i)) == k - 1)
			s += sr[j++], --k;
		s += sl[i];
	}
	return s;
}

int main() {
	for (char c : str) {
		string s(128, c);
		int t = 128 - ask(s);
		if (t) ss.pb(s.substr(0, t));
		n += t;
	}
	string ans = merge(0, ss.size() - 1);
	cout << "! " << ans << endl;
	return 0;
}

Random Pawn

首先显然从 aa 最大值的位置断开。

发现跟 这题 很像,区别在于多了个 bib_i。

即现在的式子为 fi=max⁡(ai,fi−1+fi+12−bi) f_i = \max(a_i, \frac{f_{i-1} + f_{i+1}}{2} - b_i) 考虑找到 c1…nc_{1\dots n} 满足 ci=ci−1+ci+12−bic_i = \frac{c_{i-1}+c_{i+1}}{2} - b_i。

则令 gi=fi−cig_i = f_i - c_i,vi=ai−civ_i = a_i - c_i,就变成了 gi=max⁡(vi,gi−1+gi+12) g_i = \max(v_i, \frac{g_{i-1} + g_{i+1}}{2}) 就是上面那题了,求个凸包即可。

const int N = 2e5 + 7;
int n;
__int128 A[N], B[N], a[N], b[N], c[N];
ld ans;
vi s(1, 0);

inline bool pd(int i, int j, int k) {
	return (a[j] - a[i]) * (k - i) <= (a[k] - a[i]) * (j - i);
}

int main() {
	rd(n), rda(A, n), rda(B, n);
	int p = max_element(A + 1, A + n + 1) - A;
	for (int i = p; i <= n; i++) a[i-p] = A[i], b[i-p] = B[i];
	for (int i = 1; i <= p; i++) a[n-p+i] = A[i], b[n-p+i] = B[i];
	for (int i = 2; i <= n; i++)
		c[i] = 2 * (c[i-1] + b[i-1]) - c[i-2], a[i] -= c[i], ans += c[i];
	for (int i = 1; i <= n; i++) {
		while (s.size() > 1u && pd(s[s.size()-2], s.back(), i)) s.pop_back();
		s.pb(i);
	}
	for (ui i = 1; i < s.size(); i++) {
		int l = s[i-1], r = s[i];
		for (int k = l + 1; k < r; k++)
			ans += 1.0L * ((k - l) * a[r] + (r - k) * a[l]) / (r - l);
		ans += a[r];
	}
	printf("%.12Lf\n", ans / n);
	return 0;
}

Name-Preserving Clubs

咕。

标签:DP、Trie、交互题、凸包、最短路、计算几何、记忆化搜索

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…