AtCoder Grand Contest 006 BCDEFF

AGC006

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
", x - 1); continue; }
        if (i == n + 1) { printf ("%d
", x + 1); continue; }
        if (i == n) { printf ("%d
", x); continue; }
        if (j == x - 1) j = x + 2; printf ("%d
", 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
", 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
", 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
", res), 0;
}
原文地址:https://www.cnblogs.com/whx666/p/agc006.html