0328周赛
今天终于得到了一杯奶茶,据说是Aurora家的。
T1 NKOJ 翻转数列

思路:水题,当i为奇数时向后添加Ai,否则向前添加Ai,输出答案时,若n为奇数则倒序输出,否则正序。
#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; char buf[1 << 22], *p1 = buf, *p2 = buf; #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++) template <class T> inline void rd(T &a) { char ch = getchar(); T x = 0, f = 1; while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();} while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); a = x; } deque <int> Q; int main() { while (!Q.empty()) Q.pop_back(); int n; rd(n); for (int i = 1, x; i <= n; i++) { rd(x); if (i & 1) Q.push_front(x); else Q.push_back(x); }if (n & 1) while (!Q.empty()) printf("%d ", Q.front()), Q.pop_front(); else while (!Q.empty()) printf("%d ", Q.back()), Q.pop_back(); }
T2 NKOJ 四倍数列

思路:也是水题,考虑搭配问题,显然要让4尽可能跟1搭配,2和2搭配,于是就有了一个想法,就是若能被4整除的数比被4除余1的数多或是等于,那么必然可以做到。但是发现过不了样例,仔细思考发现还有一种情况,就是能被4整除的数比被4除余1的数的个数少1个,但是没有能被2整除的数,如此也满足题意,于是这个题就做完了。
#include <bits/stdc++.h> using namespace std; int num[3]; int main() { int n; scanf("%d", &n); for (int i = 1, x; i <= n; i++) { scanf("%d", &x); if (x % 4 == 0) ++num[2]; else if (x % 2 == 0) ++num[1]; else ++num[0]; } if (num[2] >= num[0] || (num[2] == num[0] - 1 && num[1] == 0)) puts("Yes"); else puts("No"); }
T3 NKOJ 站队

思路:就是在一个序列中找最长上升子序列且公差为1。也有另外一种方法就是记录1-n在此数列中出现的位置,如此就可以找最长上升连续子序列。
#include <bits/stdc++.h> using namespace std; int a[200005], f[200005]; int main() { int n, ans = 0; scanf("%d", &n); for (int i = 1, x; i <= n; i++) scanf("%d", &x), a[x] = i; for (int i = 1; i <= n; i++) { if (a[i] > a[i - 1]) f[i] = f[i - 1] + 1; else f[i] = 1; ans = max(ans, f[i]); } printf("%d", n - ans); }
T4 NKOJ 打针

思路:一开始考虑直接从每个点往下跑,发现会被链卡成麻瓜,于是就反向考虑,从每一个点倍增往上跑,找到能跑到的最上面的点,而这条路径上所有的点都能跑到这个点,等同于说给这条路径上所有的点做出了1的贡献,所以考虑树上差分。
#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; typedef long long ll; ll n, s, dist[200005][21], fa[200005][21], dep[200005]; struct node { ll to, nxt, len; }e[400005]; ll head[200005], tot, cnt[200005]; inline void add_e(ll u, ll v, ll w) {e[++tot].to = v; e[tot].nxt = head[u]; head[u] = tot; e[tot].len = w;} void dfs(ll u) { for (ll i = 1; i <= 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1], dist[u][i] = dist[u][i - 1] + dist[fa[u][i - 1]][i - 1]; for (ll i = head[u]; i; i = e[i].nxt) { ll v = e[i].to; if (v == fa[u][0]) continue; fa[v][0] = u, dist[v][0] = e[i].len, dep[v] = dep[u] + 1; dfs(v); } } void search(ll u) { // cnt[u]++; for (ll i = head[u]; i; i = e[i].nxt) { ll v = e[i].to; if (v == fa[u][0]) continue; search(v); cnt[u] += cnt[v]; } } char buf[1 << 22], *p1 = buf, *p2 = buf; #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++) template <class T> inline void rd(T &a) { char ch = getchar(); T x = 0, f = 1; while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();} while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); a = x; } main() { rd(n), rd(s); for (ll i = 2, x, y, z; i <= n; i++) { rd(x), rd(z); add_e(x, i, z); } dfs(1); dep[0] = -1; for (ll i = 1 ; i <= n; i++) { ll dis = 0, u = i, v = i; for (ll j = 20; ~j; j--) { if (dist[u][j] + dis <= s && dep[u] >= 0) dis += dist[u][j], u = fa[u][j]; } cnt[v]++, cnt[fa[u][0]]--; } search(1); for (ll i = 1; i <= n; i++) printf("%lld\n", cnt[i]); }
T5 NKOJ 叶子的染色

思路:显然的树形dp,有两个性质:一是以n+1到m任何一个点作为根的结果是一样的,可以手推得出;二是越往上走能够控制的节点越多。有了这两条性质,我们列出状态:f[i][j]表示i节点涂j(0/1)颜色的最小代价。
于是有状态转移方程:f[i][j] += min(f[v][j] - 1, f[v][j ^ 1]) (其中v为i的儿子)(显然若儿子与父亲的颜色一样,则可以擦除儿子的颜色,所以代价减1),做完了。
#include <bits/stdc++.h> using namespace std; struct node { int to, nxt; }e[20005]; int head[10005], tot, m, n; inline void add_e(int u, int v) {e[++tot].to = v; e[tot].nxt = head[u]; head[u] = tot;} int c[5005], f[100005][2]; void dp(int u, int fa) { for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v == fa) continue; dp(v, u); for (int j = 0; j <= 1; j++) { f[u][j] += min(f[v][j] - 1, f[v][j ^ 1]); } } } int main() { scanf("%d%d", &m, &n); for (int i = 1; i <= m; i++) f[i][0] = f[i][1] = 1; for (int i = 1, x; i <= n; i++) scanf("%d", &x), f[i][x ^ 1] = 0x3f3f3f3f; for (int i = 1, x, y; i < m; i++) { scanf("%d%d", &x, &y); add_e(x, y); add_e(y, x); } dp(n + 1, 0); printf("%d", min(f[n + 1][0], f[n + 1][1])); }
难得的AK,下次继续努力!!!

浙公网安备 33010602011771号