AtCoder Beginner Contest 340

AtCoder Beginner Contest 340

![image-20240210224000170](AtCoder Beginner Contest 340.assets/image-20240210224000170.png)

稳定发挥,速度有点慢。

C - Divide and Divide

Problem Statement

黑板上写着一个整数 \(N\)

高桥将重复下面的一系列操作,直到所有不小于 \(2\) 的整数都从黑板上移除:

  • 选择写在黑板上的一个不小于 \(2\) 的整数 \(x\)
  • 擦去黑板上出现的一个 \(x\)。然后,在黑板上写下两个新的整数 \(\left \lfloor \dfrac{x}{2} \right\rfloor\)\(\left\lceil \dfrac{x}{2} \right\rceil\)
  • 高桥必须支付 \(x\) 日元才能完成这一系列操作。

这里,\(\lfloor a \rfloor\) 表示不大于 \(a\) 的最大整数,\(\lceil a \rceil\) 表示不小于 \(a\) 的最小整数。

当不能再进行操作时,高桥支付的总金额是多少?

可以证明,无论操作的顺序如何,他支付的总金额是不变的。

Solution

\(f(n)\) 表示如果最开始黑板上写着 \(n\),总金额是多少。

首先高桥会擦掉 \(n\),花费 \(n\) 的代价。

然后写上 \(\left \lfloor \dfrac{x}{2} \right\rfloor\)\(\left\lceil \dfrac{x}{2} \right\rceil\),分别花费 \(f(\left \lfloor \dfrac{x}{2} \right\rfloor)\)\(f(\left\lceil \dfrac{x}{2} \right\rceil)\) 的代价。

所以有转移 \(f(n) = n + f(\left \lfloor \dfrac{x}{2} \right\rfloor)+f(\left\lceil \dfrac{x}{2} \right\rceil)\)。记忆化搜索之。

Code

map<int, int> p;

int f(int n) {
	if (n < 2) return 0;
	if (p.count(n)) return p[n];
	return p[n] = n + f(n / 2) + f((n + 1) / 2);
}

signed main()
{
	int n = read();
	wel(f(n));
	return 0;
}

D - Super Takahashi Bros.

Problem Statement

高桥正在玩一个游戏。

游戏由编号为 \(1,2,\ldots,N\)\(N\) 个阶段组成。最初,只有阶段 \(1\) 可以玩。

对于每个可以下棋的阶段 \(i\) ( \(1\leq i \leq N-1\) ),你都可以在阶段 \(i\) 执行以下两个操作中的一个:

  • 花费 \(A_i\) 秒清除阶段 \(i\)。这样就可以进入 \(i+1\) 阶段。
  • 花费 \(B_i\) 秒清除阶段 \(i\)。这样就可以进入 \(X_i\) 阶段。

忽略通关时间以外的其他时间,至少需要多少秒才能通关 \(N\)

Solution

DP 然后记忆化搜索是不对的,因为转移成环。

直接联想图论。我们建一张有向带权图,对于其中的一条 \(u \to v\) 边权为 \(w\) 的边而言,它的含义为在花费 \(w\) 秒打完关卡 \(u\) 后,可以直接来到关卡 \(v\)

所以我们连边 \(i \to i + 1\) 边权为 \(A_i\)\(i \to X_i\) 边权为 \(B_i\)

那么根据我们建图中的定义,显然图上 \(1\)\(n\) 的最短路径长度即为答案。

使用堆优化 Dijkstra 即可。

Code

int n, a[N], b[N], x[N], w[N];
int h[N], e[N], ne[N], idx;

void add(int a, int b, int c) {
	e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}

int dis[N];
bool st[N];

void dij() {
	priority_queue<PII, vector<PII>, greater<PII> > q;
	memset(dis, 0x3f, sizeof dis);
	dis[1] = 0;
	q.push({0, 1});
	
	while (q.size()) {
		int t = q.top().se;
		q.pop();
		
		if (st[t]) continue;
		st[t] = true;
		
		for (int i = h[t]; ~i; i = ne[i]) {
			int j = e[i];
			if (dis[j] > dis[t] + w[i]) {
				dis[j] = dis[t] + w[i];
				q.push({dis[j], j});
			}
		}
	}
}

signed main()
{
	memset(h, -1, sizeof h);
	n = read();
	fup (i, 1, n - 1) {
		a[i] = read(), b[i] = read(), x[i] = read();
		add(i, i + 1, a[i]);
		add(i, x[i], b[i]);
	}
	
	dij();
	wel(dis[n]);
	
	return 0;
}

E - Mancala 2

Problem Statement

\(N\) 个编号为 \(0\)\(N-1\) 的盒子。最初,\(i\) 盒里有 \(A_i\) 个球。

高桥将依次对 \(i=1,2,\ldots,M\) 进行以下操作:

  • 将变量 \(C\) 设为 \(0\)
  • 从盒子 \(B_i\) 中取出所有的球并握在手中。
  • 在手拿至少一个球的同时,重复下面的过程:
    • \(C\) 的值增加 \(1\)
    • 将手中的一个球放入盒子 \((B_i+C) \bmod N\)

完成所有操作后,确定每个盒子中的球数。

Solution

可以发现,操作盒子 \(b_i\) 中的球时,将球放入的盒子的下标是连续的。我们令此时 \(b_i\) 这个盒子中的球有 \(x\) 个。

由于 \(x\) 非常大,可能会导致所有盒子中的球数量都增加。不难发现全局会增加 \(\lfloor \frac xn \rfloor\) 次。

剩下的显然有 \(x \bmod n\) 个。此时如果 \(x \bmod n = 0\) 那么直接下一轮即可。以下讨论的均为 \(x \bmod n \ne 0\) 的情况。

我们需要把这些球放入 \(b_i + 1, b_i + 2, \dots, 1, 2, \dots\) 这些盒子中。那么如果 \(b_i + x \le n\),也就是说不会回到前面,那么我们直接将盒子 \(b_i + 1 \sim b_i + x\) 加一即可。

否则 \(b_i + x > n\),首先盒子 \(b_i + 1 \sim n\) 都会加一,然后剩下的 \(x - (n - b_i)\) 会在最前面的几个盒子中依次填充,即盒子 \(1 \sim x - (n - b_i)\) 加一。

所以维护支持区间加,单点查询的线段树然后模拟,就做完了。

Code

int n, m, a[N], b[N];

struct Tree {
	int l, r, v, add;
}tr[N << 2];

void pushup(int u) {
	tr[u].v = tr[ls].v + tr[rs].v;
}

void calc(int u, int v) {
	tr[u].v += v * (tr[u].r - tr[u].l + 1);
	tr[u].add += v;
}

void pushdown(int u) {
	calc(ls, tr[u].add);
	calc(rs, tr[u].add);
	tr[u].add = 0;
}

void build(int u, int l, int r) {
	tr[u] = {l, r, a[l]};
	if (l == r) return;
	int mid = l + r >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
	pushup(u);
}

void modify(int u, int x) {
	if (tr[u].l == tr[u].r) tr[u].v = 0;
	else {
		int mid = tr[u].l + tr[u].r >> 1;
		pushdown(u);
		if (x <= mid) modify(ls, x);
		else modify(rs, x);
		pushup(u);
	}
}

void modify(int u, int l, int r, int d) {
	if (tr[u].l >= l && tr[u].r <= r) calc(u, d);
	else {
		int mid = tr[u].l + tr[u].r >> 1;
		pushdown(u);
		if (l <= mid) modify(ls, l, r, d);
		if (r > mid) modify(rs, l, r, d);
		pushup(u);
	}
}

int query(int u, int x) {
	if (tr[u].l == tr[u].r) return tr[u].v;
	int mid = tr[u].l + tr[u].r >> 1;
	pushdown(u);
	if (x <= mid) return query(ls, x);
	return query(rs, x);
}

signed main()
{
	n = read(), m = read();
	fup (i, 1, n) a[i] = read();
	fup (i, 1, m) b[i] = read() + 1;
	
	build(1, 1, n);
	
	fup (i, 1, m) {
		int x = query(1, b[i]);
		
		modify(1, b[i]);
		modify(1, 1, n, (x / n));
		x %= n;
		
		if (!x) continue;
		if (b[i] + x <= n) modify(1, b[i] + 1, b[i] + x, 1);
		else {
			if (b[i] == n) modify(1, 1, x, 1);
			else {
				modify(1, b[i] + 1, n, 1);
				modify(1, 1, x - n + b[i], 1);
			}
		}
	}
	
	fup (i, 1, n) wsp(query(1, i));
	
	return 0;
}

F - S = 1

Problem Statement

给你整数 \(X\)\(Y\),它们至少满足 \(X \neq 0\)\(Y \neq 0\) 中的一个。

请找出一对满足以下所有条件的整数 \((A, B)\)。如果不存在这样的一对,请报告。

  • \(-10^{18} \leq A, B \leq 10^{18}\)
  • 顶点位于 \(xy\) 平面上点 \((0, 0), (X, Y), (A, B)\) 的三角形的面积为 \(1\).

Solution

首先,三个顶点分别在 \((0, 0), (X, Y), (A, B)\) 的三角形面积为 \(\dfrac {|AY-BX|}2\)。在给定 \(X, Y\) 的情况下,我们需要求下列方程的整数解 \(A, B\)

\[AY - BX = \pm2 \]

不难发现可以用扩展欧几里得算法来解决。

首先求出最大公约数 \(d = \gcd(X, Y)\)。那么如果 \(d > 2\) 则无解,因为 \(AY - BX\) 一定是 \(d\) 的倍数,而在 \(d>2\)\(d\) 不可能是 \(\pm 2\) 的倍数。

然后我们令 \(X' = Y\)\(Y'= -X\),那么首先我们要求出下列方程的整数解 \(A, B\)

\[AX'+BY'=d \]

这就是最直接的扩展欧几里得算法所求的式子,可以直接求出 \(A, B\)

求出来后,我们希望的方程右边应该是 \(\pm 2\) 而不是 \(d\)。所以我们把方程两边同时乘 \(\dfrac 2d\)\(-\dfrac 2d\),即 \(A \gets \dfrac {2A}d, B \gets \dfrac{2B}d\)\(A \gets -\dfrac {2A}d, B \gets -\dfrac{2B}d\) 均可。

Code

int x, y, a, b;

void exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
		return;
	}
	exgcd(b, a % b, y, x);
	y -= a / b * x;
}

signed main() {
	x = read(), y = read();
	int d = __gcd(abs(x), abs(y));
	if (d > 2) return puts("-1"), 0;
	exgcd(y, -x, a, b);
	a *= 2 / d, b *= 2 / d;
	wel(a, b);
	return 0;
}
posted @ 2024-02-10 22:03  2huk  阅读(52)  评论(0编辑  收藏  举报