【CodeForces】CodeForces Round #476 (Div. 2) 题解

【比赛链接】

【题解链接】

【A】Paper Airplanes

【思路要点】

  • 按照题意计算即可。
  • 时间复杂度\(O(1)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
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("");
}
long long k, n, s, p; 
int main() {
	read(k), read(n), read(s), read(p);
	long long each = n / s + (n % s != 0);
	long long tot = each * k;
	writeln(tot / p + (tot % p != 0));
	return 0;
}

【B】Battleship

【思路要点】

  • 按照题意计算每个位置可能包含舰船的方案数,取最大值即可。
  • 时间复杂度\(O(N^2K)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
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, m;
char s[MAXN][MAXN];
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		scanf("\n%s", s[i] + 1);
	int ansx = 1, ansy = 1, ans = 0;
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= n; j++) {
		if (s[i][j] == '#') continue;
		int now = 0, cnt = 1;
		for (int k = i + 1; k <= n && k - i + 1 <= m; k++)
			if (s[k][j] == '.') cnt++;
			else break;
		for (int k = i - 1; k >= 1 && i - k + 1 <= m; k--)
			if (s[k][j] == '.') cnt++;
			else break;
		if (cnt >= m) now += cnt - m + 1;
		cnt = 1;
		for (int k = j + 1; k <= n && k - j + 1 <= m; k++)
			if (s[i][k] == '.') cnt++;
			else break;
		for (int k = j - 1; k >= 1 && j - k + 1 <= m; k--)
			if (s[i][k] == '.') cnt++;
			else break;
		if (cnt >= m) now += cnt - m + 1;
		if (now > ans) {
			ans = now;
			ansx = i;
			ansy = j;
		}
	}
	printf("%d %d\n", ansx, ansy);
	return 0;
}

【C】Greedy Arkady

【思路要点】

  • 由题目给出的限制,选取\(x=M\)始终是合法的,计算选取\(x=M\)时的收益。
  • 令Arkady获得糖果的次数为\(i\),对于同一个\(i\),显然在满足Arkady确实恰好获得了\(i\)次糖果的前提下,\(x\)取值越大,方案越优。
  • 枚举\(i\),计算对于每个\(i\)最大的\(x\)的取值,若此时\(x>M\),则忽略之,否则用其更新答案。
  • 由于我们先前计算了取\(x=M\)时的收益,我们并不需要考虑对于每个\(i\),\(x\)不取其最大取值的情况。
  • 注意可能的越界问题。
  • 时间复杂度\(O(D)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
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("");
}
long long n, k, m, d;
int main() {
	read(n), read(k), read(m), read(d);
	long long tot = k * m, tmp = n / tot;
	long long ans = m * tmp;
	if (n % tot >= m) ans += m;
	for (int i = 1; i <= d; i++) {
		long long cnt = 1 + k * (i - 1);
		if (n / cnt > m) continue;
		else if (n / cnt == 0) break;
		long long x = n / cnt;
		chkmax(ans, x * i);
	}
	writeln(ans);
	return 0;
}

【D】Single-use Stones

【思路要点】

  • 二分答案,贪心判断可行性。
  • 时间复杂度\(O(WLog\sum A_i)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
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, m, l, r, a[MAXN];
bool check(int mid) {
	static int b[MAXN], c[MAXN];
	int pos;
	for (int i = 1; i <= n; i++)
		c[i] = a[i];
	memset(b, 0, sizeof(b));
	b[pos = 0] = mid;
	for (int i = 1; i <= n; i++) {
		if (i - pos > m) return false;
		while (c[i] != 0 && pos != i) {
			int tmp = min(c[i], b[pos]);
			b[pos] -= tmp;
			b[i] += tmp; c[i] -= tmp;
			while (b[pos] == 0) pos++;
		}
	}
	return true;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n - 1; i++)
		read(a[i]), r += a[i];
	while (l < r) {
		int mid = (l + r + 1) / 2;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	writeln(l);
	return 0;
}

【E】Short Code

【思路要点】

  • 建立字典树,问题转化成给定一棵树,将一些给定的关键点向根节点移动至深度和最小。
  • 记\(dp_i\)为一个数组,表示以\(i\)为根的子树中,子问题的最优解:共有\(dp_{i,j}\)个关键点被安排在深度为\(j\)的节点上。
  • 转移分为两种,一种是将子节点的信息按位相加合并,另一种是在某一个没有关键点的位置\(x\)上将一个深度最大的关键点重新安排在\(i\)上,也即删除集合中最大的数,并加入当前节点的深度。
  • 朴素地实现上述DP,可以得到\(O(N^2)\)的时间复杂度。
  • 用平衡树(std::set)+启发式合并维护DP数组的第二维,时间复杂度降至\(O(NLog^2N)\)。
  • 用线段树维护DP数组的第二维,并用线段树合并实现转移,时间复杂度降至\(O(NLogN)\)。
  • 用长链剖分维护DP数组的第二维,时间复杂度为\(O(N)\)。
  • 以下代码笔者使用的是线段树合并的做法。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int MAXP = 1e7 + 5;
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("");
}
struct SegmentTree {
	struct Node {
		int lc, rc;
		int sum, Max;
	} a[MAXP];
	int size, n;
	void init(int x) {
		n = x;
		size = 0;
		a[0].sum = 0;
		a[0].Max = -1;
	}
	void update(int root) {
		a[root].Max = -1; a[root].sum = 0;
		chkmax(a[root].Max, a[a[root].lc].Max);
		chkmax(a[root].Max, a[a[root].rc].Max);
		a[root].sum += a[a[root].lc].sum;
		a[root].sum += a[a[root].rc].sum;
	}
	void modify(int &root, int l, int r, int pos, int d) {
		if (root == 0) root = ++size;
		if (l == r) {
			a[root].sum += d;
			if (a[root].sum) a[root].Max = l;
			else a[root].Max = -1;
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) modify(a[root].lc, l, mid, pos, d);
		else modify(a[root].rc, mid + 1, r, pos, d);
		update(root);
	}
	void modify(int &root, int pos, int d) {
		modify(root, 0, n, pos, d);
	}
	int merge(int x, int y, int l, int r) {
		if (x == 0 || y == 0) return x + y;
		if (l == r) {
			a[x].sum += a[y].sum;
			if (a[x].sum) a[x].Max = l;
			else a[x].Max = -1;
			return x;
		}
		int mid = (l + r) / 2;
		a[x].lc = merge(a[x].lc, a[y].lc, l, mid);
		a[x].rc = merge(a[x].rc, a[y].rc, mid + 1, r);
		update(x);
		return x;
	}
	int merge(int x, int y) {
		return merge(x, y, 0, n);
	}
	void trans(int root, int depth) {
		if (a[root].Max == -1) return;
		modify(root, a[root].Max, -1);
		modify(root, depth, 1);
	}
	long long getans(int root, int l, int r) {
		if (root == 0) return 0;
		if (l == r) return 1ll * a[root].sum * l;
		int mid = (l + r) / 2;
		return getans(a[root].lc, l, mid) + getans(a[root].rc, mid + 1, r);
	}
	long long getans(int root) {
		return getans(root, 0, n);
	}
} ST;
struct Trie {
	struct Node {
		int child[26];
		int depth, root;
	} a[MAXN];
	int root, size;
	void init() {
		root = 0;
		a[root].depth = 0;
		ST.modify(a[root].root, 0, 1);
	}
	void insert(char *s) {
		int len = strlen(s + 1), now = root;
		for (int i = 1; i <= len; i++) {
			int tmp = s[i] - 'a';
			if (a[now].child[tmp] == 0) {
				a[now].child[tmp] = ++size;
				a[size].depth = a[now].depth + 1;
			}
			now = a[now].child[tmp];
		}
		ST.modify(a[now].root, a[now].depth, 1);
	}
	long long getans() {
		static int q[MAXN], cnt[MAXN];
		for (int i = 0; i <= size; i++)
			cnt[a[i].depth]++;
		for (int i = 1; i <= size; i++)
			cnt[i] += cnt[i - 1];
		for (int i = 0; i <= size; i++)
			q[--cnt[a[i].depth]] = i;
		for (int i = size; i >= 0; i--) {
			int tmp = q[i];
			bool type = a[tmp].root == 0;
			for (int j = 0; j < 26; j++)
				if (a[tmp].child[j]) a[tmp].root = ST.merge(a[tmp].root, a[a[tmp].child[j]].root);
			if (type) ST.trans(a[tmp].root, a[tmp].depth);
		}
		return ST.getans(a[root].root);
	}
} Trie;
int n; char s[MAXN];
int main() {
	read(n);
	ST.init(MAXN - 1);
	Trie.init();
	for (int i = 1; i <= n; i++) {
		scanf("%s", s + 1);
		Trie.insert(s);
	}
	writeln(Trie.getans());
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80158550