AtCoder Grand Contest 006 BCDEFF
B&D Median Pyramid Easy&Hard
B和D关键步骤差不多,放一起了
假设已知底部的元素,如何判定顶部的数是否 \(\ge x\) ?
把 \(<x\) 的数赋为 \(0\) ,反之为 \(1\),因为只要判断是否在范围内,和具体值无关
结论:如果有两个 \(1\) 相邻,则这两竖全都是 \(1\),\(0\) 也是一样
看一下离中点最近的 "0对" 和 “1对”,如果 “1对” 更近,顶端的数 \(\ge x\),否则 \(<x\)
如果不存在 "0对" 和 “1对”,\(0,1\) 间隔存在,特判一下
对于D题,二分答案 \(x\),判断答案是否 \(\ge x\)
对于B题,要构造出底端的数组,判定结果 \(\ge x\) 且 \(<x+1\) ,可以想到最中间三个元素为 \(x,x-1,x+1\),其他的随意,如果 \(x\) 是最小值或最大值不存在解
B:
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 2e5 + 5;
int n, x, a[N];
signed main() {
read (n), read (x);
if (x == 1 || x == 2 * n - 1) return puts ("No"), 0;
puts ("Yes");
for (int i = 1, j = 1; i <= 2 * n - 1; ++i) {
if (i == n - 1) { printf ("%d\n", x - 1); continue; }
if (i == n + 1) { printf ("%d\n", x + 1); continue; }
if (i == n) { printf ("%d\n", x); continue; }
if (j == x - 1) j = x + 2; printf ("%d\n", j), ++j;
}
return 0;
}
D
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
}
int n, l, r, mid, ans, a[200010];
bool check () {
for (int i = 0; i < n; ++i) {
if ((a[n-i] < mid and a[n-i-1] < mid) or (a[n+i] < mid and a[n+i+1] < mid))
return false;
if ((a[n-i] >= mid and a[n-i-1] >= mid) or (a[n+i] >= mid and a[n+i+1] >= mid))
return true;
}
return a[n*2-1] >= mid;
}
signed main() {
read (n);
for (int i = 1; i <= (n << 1) - 1; ++i) read (a[i]);
l = 1, r = (n << 1) - 1;
while (l <= r) {
mid = l + r >> 1;
if (check()) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf ("%d\n", ans);
return 0;
}
C - Rabbit Exercise
对 \(x\) 操作一次后,\(x\) 的期望位置 \(f_x=\frac{1}{2}((2f_{x-1}-f_x)+(2f_{x+1}-f_x))=f_{x+1}+f_{x-1}-f_x\)
进行差分,\(g_i=f_i-f_{i-1}\),执行一次操作 \(x\) 相当于 \(swap(g_x,g_{x+1})\)
先处理出一轮操作后的位置变化,然后倍增算出最后的位置变化,按照差值推一遍
#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
char ch = getchar(); int f = 0; x = 0;
while (!isdigit(ch)) { if (ch == '-') f = 1; ch = getchar(); }
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar(); if (f) x = -x;
} const int N = 1e5 + 5;
int n, m, k, a[N], p[N], d[N], now[N], t[N];
void change (int *a, int *b) {
for (int i = 2; i <= n; ++i) t[i] = a[b[i]];
memcpy (a, t, sizeof (t));
}
signed main() {
read (n);
for (int i = 2; i <= n; ++i) p[i] = now[i] = i;
for (int i = 1; i <= n; ++i) read (a[i]);
for (int i = 2; i <= n; ++i) d[i] = a[i] - a[i - 1];
read (m), read (k);
for (int i = 1, x; i <= m; ++i)
read (x), swap (p[x], p[x + 1]);
while (k) {
if (k & 1) change (now, p);
change (p, p), k >>= 1;
}
for (int i = 1, res = a[1]; i <= n; ++i)
printf ("%lld\n", res), res += d[now[i + 1]];
return 0;
}
E - Rotate 3x3
首先,一些明显的无解:
1、某一列内的三个数不应该在同一列中
2、这一列应该在的列和当前在的列奇偶性不同(隔一个交换不改变奇偶性)
把奇偶列分开,然后发现:可以仅翻转任意两个奇数列的上下顺序而不改变其他列的上下、左右顺序,偶数列同理
具体做法是:假设要翻转两个奇数列 \(x,y\),先把 \(x\) 若干此翻转到 \(y\) 的旁边(隔一列),对中间一列操作,再把 \(x\) 弄回去。
先统计一下奇偶列分别需要上下翻转的次数,但在排序偶数列的过程中对奇数列有影响,还要减去排序用的交换次数。如果奇偶剩下的都是个偶数(可以为负)就可以
求排序过程中交换次数的奇偶性方法就很多了...
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5;
int n, s[2], c[3][N], p[N], a[N];
#define fail return puts ("No"), 0
signed main() {
read (n);
for (int i = 0; i < 3; ++i)
for (int j = 1; j <= n; ++j) read (c[i][j]);
for (int i = 1; i <= n; ++i) {
int tag = 0, id = (c[1][i] + 2) / 3;
if (c[0][i] % 3 == 0) tag = 1, s[i & 1] ^= 1, swap (c[0][i], c[2][i]);
if ((i & 1) != (id & 1)) fail;
for (int j = 0; j < 3; ++j)
if (c[j][i] != 3 * id - 3 + j + 1) fail;
a[i] = id, p[id] = i;
}
for (int i = 1; i <= n; ++i) {
if (a[i] == i) continue;
p[a[i]] = p[i], swap (a[i], a[p[i]]);
s[i & 1 ^ 1] ^= 1;
}
if (s[0] || s[1]) fail;
return puts ("Yes"), 0;
}
F - Blackout
先说结论:对每一个连通块三染色,即把 \(R\) 点连向的点染成 \(G\),\(G\) 连向 \(B\),\(B\) 连向 \(R\),如果可以成功染色:
1、三种颜色都有,边的数量是 \(cnt(R)*cnt(G)+cnt(G)*cnt(B)+cnt(B)*cnt(R)\)
2、没有三种颜色,不能连新边,就是原来边的数量
如果不能成功染色,最后会变成一张完全图
为什么是对的?可以成功染色且没有三种颜色的情况显然正确,另外两种情况说一下大致做法,具体的要自己举例实践感受一下
可以染色且三种颜色都有:把连通块每三层划为一段,先考虑每一段内有那些边可以连,然后和相邻的段进行合并,看一看哪些边可以连。
如果不能染色,存在一个长度不是 \(3\) 的倍数的环,这样的环肯定可以形成完全图,然后从环出发向外拓展点,所有边都是可以连上的
#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5, M = N << 1;
int n, m, res, tag, s, cc, co[N], c[3];
int cnt, k[M], to[M], h[M], nxt[M];
void add (int u, int v) {
to[++cnt] = v, k[cnt] = 1, nxt[cnt] = h[u], h[u] = cnt;
to[++cnt] = u, k[cnt] = -1, nxt[cnt] = h[v], h[v] = cnt;
}
int f (int x, int k) {
x += k; if (x == -1) x = 2; if (x == 3) x = 0; return x;
}
void dfs (int u) {
++c[co[u]], ++s;
for (int i = h[u], v; i; i = nxt[i]) {
cc += k[i] == 1;
if (co[v = to[i]] < 0) co[v] = f (co[u], k[i]), dfs (v);
else if (co[v] != f (co[u], k[i])) tag = 0;
}
}
signed main() {
read (n), read (m);
for (int i = 1, u, v; i <= m; ++i)
read (u), read (v), add (u, v);
memset (co, -1, sizeof (co));
for (int i = 1; i <= n; ++i) {
if (co[i] >= 0) continue;
c[0] = c[1] = c[2] = s = cc = 0; tag = 1; co[i] = 0, dfs (i);
if (!tag) res += s * s;
else if (c[0] && c[1] && c[2]) res += c[0] * c[1] + c[1] * c[2] + c[2] * c[0];
else res += cc;
}
return printf ("%lld\n", res), 0;
}