AtCoder Regular Contest 195 (Div. 2)

ARC195A Twice Subsequence

找到第一次和最后一次出现的位置,判断是否相同即可。

void slv() {
	int n, m;
	Read(n, m);
	
	vector<int> A(n), nxt(n), B(m);
	for (int i = 0; i < n; i ++) {
		Read(A[i]);
	}
	for (int i = 0; i < m; i ++) {
		Read(B[i]);
	}
	
	auto check = [&](vector<int> A, vector<int> B) {
		map<int, vector<int>> pos;
		for (int i = 0; i < n; i ++) {
			pos[A[i]].emplace_back(i);
		}
		vector<int> F;
		int now = 0;
		for (int i = 0; i < m; i ++) {
			if (!pos.contains(B[i])) break;
			auto &vec = pos[B[i]];
			auto it = lower_bound(vec.begin(), vec.end(), now);
			if (it == vec.end()) break;
			F.emplace_back(*it), now = *it + 1;
		}
		return F;
	};
	
	auto F = check(A, B);
	reverse(A.begin(), A.end());
	reverse(B.begin(), B.end());
	auto G = check(A, B);
	reverse(G.begin(), G.end());
	for (auto &x : G) x = n - x - 1;
	if (F.size() < m || G.size() < m || F == G) No();
	else Yes();
	
	return;
}

ARC195B Uniform Sum

\(-1\) 和任意数匹配都是合法的,记 \(A\)\(-1\) 的个数为 \(x\)\(B\) 中为 \(y\),那么只需要找到 \(n - x - y\) 对匹配使得和相等即可。

可以通过 map 记一下每种值有多少个然后暴力。

void slv() {
	
	int n = Read<int>();
	
	vector<int> A(n), B(n);
	for (int i = 0; i < n; i ++) Read(A[i]);
	for (int i = 0; i < n; i ++) Read(B[i]);
	
	int cA = count(A.begin(), A.end(), -1),
		cB = count(B.begin(), B.end(), -1);
	
	if (cA + cB >= n) {
		Yes(); return;
	}
	
	map<int, int> mA, mB, mC;
	for (auto x : A) if (~x) mA[x] ++;
	for (auto x : B) if (~x) mB[x] ++;
	for (auto [a, ca] : mA)
		for (auto [b, cb] : mB)
			mC[a + b] += min(ca, cb);
	
	const int mx = max(*max_element(A.begin(), A.end()),
		*max_element(B.begin(), B.end()));
	
	for (auto [x, c] : mC) {
		if (x >= mx && c + cA + cB >= n) {
			Yes(); return;
		}
	}
	No();
	
	return;
}

ARC195C Hamiltonian Pieces

先考虑合法条件,进行黑白染色,那么 R 的影响会改变颜色,B 不会变色,所有首先要 R 的个数为偶数,在没有 R 的时候,同理可以得到 B 的个数必须是偶数。

其余情况都有解。

分四种情况构造:

  • \(r = 0, 2 \mid b\):构造一个斜的长方形即可。
  • \(2 \mid r, b = 0\):构造一个长方形。
  • \(2 \mid r, 2 \mid b, b > 0\):构造一个平行四边形。
  • \(2 \mid r, 2 \nmid b\):用 \(r\) 构造一个长方形,然后把右上角切掉,拼上一个 \(b\) 构造的斜的长方形。
void slv() {
	
	int r, b;
	Read(r, b);
	
	if (r & 1) {
		No();
		return;
	}
	
	if (r == 0) {
		if (b & 1) {
			No();
			return;
		}
		if (b == 2) {
			Yes();
			Write('B', ' ', 1, ' ', 1, '\n');
			Write('B', ' ', 2, ' ', 2, '\n');
			return;
		} else {
			Yes();
			int half = b / 2;
			int x = 1, y = 1e7;
			for (int i = 0; i < half; i ++) {
				Write('B', ' ', x + i, ' ', y - i, '\n');
			}
			x ++, y ++;
			for (int i = half - 1; i >= 0; i --) {
				Write('B', ' ', x + i, ' ', y - i, '\n');
			}
			return;
		}
	}
	
	int halfr = r / 2;
	if (b == 0) {
		int x = 1, y = 1; Yes();
		for (int j = 0; j < halfr; j ++) {
			Write('R', ' ', x + j, ' ', y, '\n');
		}
		++ y;
		for (int j = halfr - 1; j >= 0; j --) {
			Write('R', ' ', x + j, ' ', y, '\n');
		}
		return;
	}
	if (!(b & 1)) {
		int halfb = b / 2;
		int x = 1, y = 1e7;
		Yes();
		for (int i = 0; i < halfr; i ++) {
			Write('R', ' ', x + i, ' ', y, '\n');
		}
		for (int i = 0; i < halfb; i ++) {
			Write('B', ' ', x + halfr + i, ' ', y - i, '\n');
		}
		++ x;
		for (int i = halfr - 1; i >= 0; i --) {
			Write('R', ' ', x + halfb + i, ' ', y - halfb, '\n');
		}
		for (int i = halfb - 1; i >= 0; i --) {
			Write('B', ' ', x + i, ' ', y - i - 1, '\n');
		}
		return;
	}
	
	int halfb = b / 2;
	int x = 1, y = 1;
	Yes();
	for (int i = 0; i < halfr; i ++) {
		Write('R', ' ', x + i, ' ', y, '\n');
	}
	for (int i = 0; i <= halfb; i ++) {
		Write('B', ' ', x + i + halfr, ' ', y + i, '\n');
	}
	for (int i = halfb - 1; i >= 0; i --) {
		Write('B', ' ', x + i + halfr, ' ', y + i + 2, '\n');
	}
	for (int i = halfr - 1; i >= 0; i --) {
		Write('R', ' ', x + i, ' ', y + 1, '\n');
	}
	return;
}

ARC195D Swap and Erase

结论是不会进行相邻的交换操作。

感性理解一下就是如果一个颜色段长度 \(\ge 2\),那么你换过去一定不会非常赚。

然后随便 DP 一下就对了。

constexpr int inf = 1E9;

void slv() {
	int n = Read<int>();
	
	vector<int> A(n);
	for (int i = 0; i < n; i ++) {
		Read(A[i]);
	}
	
	vector<array<int, 2>> f(n);
	f[0][0] = 1, f[0][1] = inf;
	for (int i = 1; i < n; i ++) {
		f[i][0] = f[i][1] = inf;
		cmin(f[i][0], f[i - 1][0] + (A[i] != A[i - 1]));
		if (i >= 2) {
			cmin(f[i][0], f[i - 1][1] + (A[i] != A[i - 2]));
			cmin(f[i][1], f[i - 2][0] + 3 - (A[i - 2] == A[i]) - (A[i] == A[i - 1]));
		}
		if (i >= 3) {
			cmin(f[i][1], f[i - 2][1] + 3 - (A[i - 3] == A[i]) - (A[i] == A[i - 1]));
		}
	}
	
	Write(min(f[n - 1][0], f[n - 1][1]), '\n');
	
	return;
}

ARC195E Random Tree Distance

好像是经典模型(?),感觉见过很多次了。

先转期望,拆一下期望,可以得到 \(\displaystyle E(\operatorname{dist}(u, v)) = E(dep_u) + E(dep_v) - 2 E(dep_{\operatorname{lca}(u, v)})\)

其中 \(E(dep_u)\) 是好算的,我们有递推关系:

\[E(dep_u) = A_u + \frac{1}{u - 1} \sum_{1 \le v < u} E(dep_v) \]

问题在于 \(E(dep_{\operatorname{lca}(u, v)})\)

事实上这个你让 \(u, v\) 随便跳然后钦定一个 \(\operatorname{lca}\) 是不可行的,因为 \(u, v\) 第一次相遇之后就不独立了,后面再随便跳就全错了。

不妨设 \(u < v\),考虑先给前 \(u\) 个点定一下父亲,并让 \(u\) 跳到根,把沿途的点标记一下,那么 \(v\) 往上跳跳到第一个标记的点就是 \(\operatorname{lca}\)

发现这个只跟 \(v\) 第一次跳到的前 \(u\) 个中的点有关,因为前 \(u\) 个的爹已经定好了,而 \(v\) 跳到前 \(u\) 个点中每个点的概率都是 \(\displaystyle \frac{1}{u}\),所以 \(E(dep_{\operatorname{lca}(u, v)}) = E(dep_{\operatorname{lca}(u, > u)})\)

那么也可以同样得到递推:

\[E(dep_{\operatorname{lca}(u, > u)}) = \frac{1}{u} \left(E(dep_u) + \sum_{1 \le v < u} E(dep_{\operatorname{lca}(v, > v)})\right) \]

这样就是线性的。

void slv() {
	int n, q;
	Read(n, q);
	
	vector<int> A(n);
	for (int i = 1; i < n; i ++) {
		Read(A[i]);
	}
	
	vector<mint> dep(n), d(n);
	mint sum = 0;
	for (int u = 1; u < n; u ++) {
		dep[u] = A[u] + sum * comb.inv(u);
		sum += dep[u];
	}
	sum = 0;
	for (int u = 0; u + 1 < n; u ++) {
		d[u] = comb.inv(u + 1) * (dep[u] + sum);
		sum += d[u];
	}
	
	while (q --) {
		int u, v; Read(u, v), -- u, -- v;
		mint E = dep[u] + dep[v] - 2 * d[u];
		Write((int)(E * comb.fac(n - 1)), '\n');
	}
	
	return;
}
posted @ 2025-03-24 11:09  definieren  阅读(157)  评论(0)    收藏  举报