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_{\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)})\)。
那么也可以同样得到递推:
这样就是线性的。
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;
}

浙公网安备 33010602011771号