USACO2024JAN 三组连打

假的,只连打了两组。Ag 没时间了。日后再补吧。

无意中存了题面,但代码大部分因为系统还原消失了,只有文字题解,将就着看吧。


Cu - A

省流:任意区间内,若某元素出现个数严格大于区间长度一半,则可将整个区间推平为该值。问最终可以使整个序列被推平为哪些值。

注意到当任意长度 \(\ge 2\) 的区间可以被推平为某种元素时,整个数列都可以被推平为该元素。故目标转化为对于某种元素判定是否存在一个可被其推平的区间。

统计元素个数采用前缀和。令 \(s_i\) 表示 \(h_i\) 在前 \(i\) 项中出现的次数,假设有 \([j,i]\) 满足条件,贪心可知 \(h_i=h_j\)

那么由定义有 \(i-j+1<2\times (s_i-s_j+1)\)。典中典,直接移项分离变量。则有 \(i-2\times s_i-1<j-2\times s_j\)。令 \(t_p\gets p-2\times s_p\),对于每个 \(h\) 记录 \(t_j\) 最大值查看是否有 \(i,j\) 满足条件即可。

Cu - B

省流:有 \(N\) 个格子,从 \(s\) 格子开始以 \(1\) 为初始能量向右跳,跳一步的距离为能量大小。格子分两种,一种经过加一定能量并反向,另一种若当前能量大于一定值则可永久摧毁,问跳出范围或无限长时间后可摧毁格子个数。

不难发现若忽略增加能量为 \(0\) 的跳板则每经过一个跳板可跳距离增加 \(1\),最多增加到 \(n\),否则会跳出去。

注意到调和级数,故直接模拟跳的过程。唯一导致时间无限的情况是存在相邻的增加能量为 \(0\) 的跳板,但其实它具体是什么并不重要,反正我们跳的次数严格大于调和级数后就可以认为进入死循环,直接结束模拟即可。我这里嫌麻烦直接拿了 \(2\times 10^8\) 作阈值。

Cu - C

省流:定义一次操作为选取一个整数 \(\Delta\le N\),并从 \(N\)\(1\),令 \(a_i\gets a_i+\Delta\) 并令 \(\Delta\)\(0\) 靠近 \(1\)\(\Delta=0\) 时停止。问令所有 \(a_i=0\) 所需最少操作次数。

挺有意思的思维题,首先需要进行一个思维转化。\(\Delta\le N\) 是一个利于解题的限制,这意味着我们想让任何一个 \(a_i\) 改变 \(1\) 而不影响到之前的值,从让 \(a_1\gets 0\) 入手,进行一次操作后每个 \(a_i\) 分到的 \(\Delta\) 应依次加 1 或依次减 1。则差分数组为 \(0\) 后跟着一截 \(1\) 是理想状态。中间每有一项不满足规律都会带来额外的操作次数。

归纳为差分数组的差分数组绝对值之和即为答案。


Ag - A

省流:有若干条限制,每条形如 \(\max\limits_{i=1}^{a_h-1}\{A_i\}=\max\limits_{i=1}^{a_j}\{A_i\}\)\(A_{a_h}>\max\limits_{i=1}^{a_h-1}\{A_i\}\),部分数已知,构造出符合条件且字典序最小的序列。

是本场最难题吧,但也没啥卡的。画个线段图容易发现,若将 \([a,h)\) 视作一条线段,那么除非 \(h\) 相同,否则两条线段不能有交集。不然的话就无解。以及如果存在不满足条件的定值也显然无解。

从前往后看每个 \(h\) 并尝试赋值,对于每个 \(1\sim a\) 记录一个需要满足的最大值数值,按照此数值从后往前填空格。

填完过后扫一遍看看是不是全部合法,可以证明若此时不合法则无解。

Ag - B

省流:一棵树,每个点上有若干个物品,对于每条从根到叶子的简单路径,可以选择路径上的一个物品,每个物品只能被选一次,问最多可选物品数。

如果一个点引导的子树下所有叶子有没有分配到的,就可以把这个点的物品分配给该叶子。

跑一个树形 DP 即可。

namespace XSC062 {
using namespace fastIO;
const int maxn = 1e5 + 5;
int n, x, y;
int cnt[maxn];
std::vector<int> g[maxn];
int a[maxn], f[maxn], p[maxn];
int min(int x, int y) {
	return x < y ? x : y;
}
void DFS(int x, int fa) {
	if ((int)g[x].size() == 1)
		cnt[x] = 1;
	for (auto i : g[x]) {
		if (i == fa) continue;
		DFS(i, x);
		f[x] += f[i];
		cnt[x] += cnt[i];
	}
	if (f[x] < cnt[x])
		f[x] = min(f[x] + a[x], cnt[x]);
	return;
}
void add(int x, int y) {
	g[x].push_back(y);
	return;
}
int main() {
	read(n);
	for (int i = 1; i <= n; ++i) read(p[i]);
	for (int i = 1; i < n; ++i) {
		read(x), read(y);
		add(x, y), add(y, x);
	}
	int tot = 0;
	for (int i = 2; i <= n; ++i)
		tot += ((int)g[i].size() == 1);
	for (int i = 1; i <= tot; ++i) ++a[p[i]];
	DFS(1, -1);
	print(f[1], '\n');
	return 0;
}
} // namespace XSC062

Ag - C

省流:对于给定的序列 \(a\),找出所有满足 \(a_i\bmod L\) 的值的种类最多为 3 的 \(L\)

也是挺有意思的数学题了。若将 \(a_i\) 按照模 \(L\) 的情况分组,则对于任意一个 \(a_i\),在 \((a_i,a_i+L)\) 中最多包含两个分别来自其余两组的数。

对于去重后 \(n>3\) 的情况,由鸽巢得必定有两个数可分为一组。故我们枚举可能的组间间隔,而可能的 \(L\) 就是这些间隔的因数。

由上面我们推出一个合法组间间隔中最多间隔三个数,我们将所有 \(a_{i+3}-a_i\)\(a_{i+2}-a_i\)\(a_{i+1}-a_i\) 纳入考虑范围即可。对于所有可能的 \(L\),直接 \(\mathcal O(n)\) 跑一个 check 检查是否合法。

因子个数照理来说是 \(\sqrt{V}\times n\) 级别的,但是实测 \(n\) 最多只有一百多。估计是因为 \(n\) 太大就很难构造出更多的合法解吧。

#define int long long
namespace XSC062 {
using namespace fastIO;
const int maxn = 1e4 + 5;
const int maxm = 3e4 + 5;
std::set<int> u;
int n, res, mn, tot;
int a[maxn], b[maxm];
bool check(int x) {
	int l1 = 0, l2 = 0, l3 = 0;
	for (int i = 1; i <= n; ++i) {
		if (l1 == 0) l1 = a[i];
		else if (x && (a[i] - l1) % x == 0) l1 = a[i];
		else if (l2 == 0) l2 = a[i];
		else if (x && (a[i] - l2) % x == 0) l2 = a[i];
		else if (l3 == 0) l3 = a[i];
		else if (x && (a[i] - l3) % x == 0) l3 = a[i];
		else return 0;
	}
	return 1;
}
int main() {
	read(n);
	for (int i = 1; i <= n; ++i) {
		read(a[i]);
		if (!mn || a[i] / 4 < mn) mn = a[i] / 4;
	}
	std::sort(a + 1, a + n + 1);
	n = std::unique(a + 1, a + n + 1) - a - 1;
	if (check(0)) {
		print(mn * (mn + 1) / 2, '\n');
		return 0;
	}
	for (int i = 2; i <= n; ++i) {
		b[++tot] = a[i] - a[i - 1];
		if (i >= 3) b[++tot] = a[i] - a[i - 2];
		if (i >= 4) b[++tot] = a[i] - a[i - 3];
	}
	std::sort(b + 1, b + tot + 1);
	tot = std::unique(b + 1, b + tot + 1) - b - 1;
	for (int i = 1; i <= tot; ++i) {
		if (check(b[i])) {
			for (int j = 1; j * j <= b[i]; ++j) {
				if (b[i] % j == 0)
					u.insert(j), u.insert(b[i] / j);
			}
		}
	}
	for (auto i : u) {
		if (i > mn) break;
		res += i;
	}
	print(res, '\n');
	return 0;
}
} // namespace XSC062
#undef int
posted @ 2024-01-31 14:28  XSC062  阅读(92)  评论(2编辑  收藏  举报