这场比赛全程心态爆炸……
开场脑子秀逗签到题WA了一发。之后0贡献。
前期状态全无 H题想复杂了,写了好久样例过不去。
然后这题还是队友过的……
后期心态炸裂,A题后缀数组理解不深,无法特判k = 1时的情况。
然后也没有心思读题了,心静不下来。
Problem B
$ans = k(n - k + 1)$
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL n, k; int main(){ while (~scanf("%lld%lld", &n, &k)){ printf("%lld\n", k * (n - k + 1)); } return 0; }
Problem D
对原序列求一遍最长不下降子序列,计长度为x
同时求一遍最长不上升子序列,计长度为y
则 $max(x, y) + k >= n$ 时符合题意。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; const int N = 1e5 + 10; int n, k; int a[N], c[N], f[N]; int T, ans; void update(int x, int val){ for (; x <= 100001; x += x & -x) c[x] = max(c[x], val); } int query(int x){ int ret = 0; for (; x; x -= x & -x) ret = max(ret, c[x]); return ret; } int main(){ scanf("%d", &T); while (T--){ scanf("%d%d", &n, &k); rep(i, 1, n) scanf("%d", a + i); memset(c, 0, sizeof c); memset(f, 0, sizeof f); rep(i, 1, n){ f[i] = query(a[i]) + 1; update(a[i], f[i]); } ans = 0 ; rep(i, 1, n) ans = max(ans, f[i]); memset(f, 0, sizeof f); memset(c, 0, sizeof c); dec(i, n, 1){ f[i] = query(a[i]) + 1; update(a[i], f[i]); } rep(i, 1, n) ans = max(ans, f[i]); if (ans + k >= n) puts("A is a magic array."); else puts("A is not a magic array."); } return 0; }
Problem E
$ans = F(2x + 3) - 1$
其中F()为斐波那契数列
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const LL mod = 998244353; struct Matrix{ LL arr[3][3];} mul, num, unit; LL k; Matrix Mul(Matrix a, Matrix b){ Matrix c; rep(i, 1, 2) rep(j, 1, 2){ c.arr[i][j] = 0; rep(k, 1, 2) (c.arr[i][j] += (a.arr[i][k] * b.arr[k][j] % mod)) %= mod; } return c; } Matrix Pow(Matrix a, LL k){ Matrix ret(unit); for (; k; k >>= 1, a = Mul(a, a)) if (k & 1) ret = Mul(ret, a); return ret; } int main(){ mul.arr[1][1] = 1; mul.arr[1][2] = 1; mul.arr[2][1] = 1; mul.arr[2][2] = 0; rep(i, 1, 2) unit.arr[i][i] = 1; num.arr[1][1] = 1; num.arr[2][1] = 1; while (~scanf("%lld", &k)){ Matrix P = Pow(mul, 2 * k + 1); Matrix c = Mul(P, num); printf("%lld\n", (c.arr[1][1] + mod - 1) % mod); } return 0; }
Problem H
体现我低下的智商的一道题……
这题还是走了不少弯路(看来我对树型DP的理解还不深入)
我的方法是考虑两种情况。
第一种情况是从某个点买入然后在以他为根的子树中的某个结点卖出。
或者从某个点卖出然后在以他为根的子树中的某个结点买入。
设这个点为x,以他为根的子树中的某个结点为y。
则收益1 = $s[x] - s[y] + a[y] - a[x] = (s[x] - a[x]) - (s[y] - a[y])$
收益2 = $s[x] - s[y] + a[x] - a[y] = (s[x] + a[x]) - (s[y] + a[y])$
其中s[i]为根结点到i的路程长度。
对某个x,要使收益1最大,则$(s[y] - a[y])$要最小。
对某个x,要使收益2最大,则$(s[y] + a[y])$要最小。
那么我们预处理出每个点这两个值,建立ST表,
然后对每个点求出前序DFS序和后序DFS序,
区间查询最小值。
那么第一种情况就解决了。
第二种情况,枚举每个点,求LCA为这个点的两个点之间的最优解。
树型DP就可以了。
以上是我在比赛的时候的想法(真的太麻烦了)
Code如下(比赛的时候还没写完,赛后稍稍fix了下就过了)
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; typedef pair<int, int> PII; const int N = 1e5 + 10; const int A = 19; int T, n; int ti, ans; int f[N][A], g[N][A], a[N], c[N], d[N], F[N], G[N], s[N], l[N], r[N], fl[N]; vector <PII> v[N]; void dfs(int x, int fa, int now){ s[x] = now; l[x] = ++ti; for (auto cnt : v[x]){ int u = cnt.fi, w = cnt.se; if (u == fa) continue; dfs(u, x, now + w); } r[x] = ti; } void ST(){ rep(i, 1, n) f[i][0] = c[fl[i]]; rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); rep(i, 1, n) g[i][0] = d[fl[i]]; rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) g[i][j] = min(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]); } inline int solve_f(int l, int r){ int k = (int)log2((double)(r - l + 1)); return min(f[l][k], f[r - (1 << k) + 1][k]); } inline int solve_g(int l, int r){ int k = (int)log2((double)(r - l + 1)); return min(g[l][k], g[r - (1 << k) + 1][k]); } void ask(int x){ ans = max(ans, c[x] - solve_f(l[x], r[x])); ans = max(ans, d[x] - solve_g(l[x], r[x])); } void dfs2(int x, int fa){ F[x] = c[x]; G[x] = d[x]; for (auto cnt : v[x]){ int u = cnt.fi; if (u == fa) continue; dfs2(u, x); F[x] = min(F[x], F[u]); G[x] = min(G[x], G[u]); } } void dp(int x, int fa){ int m1 = 1 << 30; int m2 = 1 << 30; int yy = 0; for (auto cnt : v[x]){ int u = cnt.fi; if (u == fa) continue; if (yy){ ans = max(ans, 2 * s[x] - F[u] - m2); ans = max(ans, 2 * s[x] - G[u] - m1); } dp(u, x); m1 = min(m1, F[u]); m2 = min(m2, G[u]); yy = 1; } } int main(){ scanf("%d", &T); while (T--){ scanf("%d", &n); rep(i, 0, n + 1) v[i].clear(); rep(i, 1, n) scanf("%d", a + i); rep(i, 2, n){ int x, y, z; scanf("%d%d%d", &x, &y, &z); v[x].push_back(MP(y, z)); v[y].push_back(MP(x, z)); } memset(s, 0, sizeof s); memset(l, 0, sizeof l); ti = 0; dfs(1, 0, 0); rep(i, 1, n) c[i] = s[i] + a[i]; rep(i, 1, n) d[i] = s[i] - a[i]; rep(i, 1, n) fl[l[i]] = i; ST(); ans = 0; rep(i, 1, n) ask(i); memset(F, 0, sizeof F); memset(G, 0, sizeof G); dfs2(1, 0); dp(1, 0); printf("%d\n", ans); } return 0; }
赛后想想,其实根本不需要ST表……
直接来个预处理,第一种情况就解决了
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; typedef pair<int, int> PII; const int N = 1e5 + 10; const int A = 19; int T, n; int ti, ans; int f[N][A], g[N][A], a[N], c[N], d[N], F[N], G[N], s[N], l[N], r[N], fl[N]; vector <PII> v[N]; void dfs(int x, int fa, int now){ s[x] = now; l[x] = ++ti; for (auto cnt : v[x]){ int u = cnt.fi, w = cnt.se; if (u == fa) continue; dfs(u, x, now + w); } r[x] = ti; } void ST(){ rep(i, 1, n) f[i][0] = c[fl[i]]; rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); rep(i, 1, n) g[i][0] = d[fl[i]]; rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) g[i][j] = min(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]); } inline int solve_f(int l, int r){ int k = (int)log2((double)(r - l + 1)); return min(f[l][k], f[r - (1 << k) + 1][k]); } inline int solve_g(int l, int r){ int k = (int)log2((double)(r - l + 1)); return min(g[l][k], g[r - (1 << k) + 1][k]); } void ask(int x){ ans = max(ans, c[x] - solve_f(l[x], r[x])); ans = max(ans, d[x] - solve_g(l[x], r[x])); } void dfs2(int x, int fa){ F[x] = c[x]; G[x] = d[x]; for (auto cnt : v[x]){ int u = cnt.fi; if (u == fa) continue; dfs2(u, x); F[x] = min(F[x], F[u]); G[x] = min(G[x], G[u]); } } void dp(int x, int fa){ int m1 = 1 << 30; int m2 = 1 << 30; int yy = 0; for (auto cnt : v[x]){ int u = cnt.fi; if (u == fa) continue; if (yy){ ans = max(ans, 2 * s[x] - F[u] - m2); ans = max(ans, 2 * s[x] - G[u] - m1); } dp(u, x); m1 = min(m1, F[u]); m2 = min(m2, G[u]); yy = 1; } } int main(){ scanf("%d", &T); while (T--){ scanf("%d", &n); rep(i, 0, n + 1) v[i].clear(); rep(i, 1, n) scanf("%d", a + i); rep(i, 2, n){ int x, y, z; scanf("%d%d%d", &x, &y, &z); v[x].push_back(MP(y, z)); v[y].push_back(MP(x, z)); } memset(s, 0, sizeof s); memset(l, 0, sizeof l); ti = 0; dfs(1, 0, 0); rep(i, 1, n) c[i] = s[i] + a[i]; rep(i, 1, n) d[i] = s[i] - a[i]; rep(i, 1, n) fl[l[i]] = i; ST(); ans = 0; rep(i, 1, n) ask(i); memset(F, 0, sizeof F); memset(G, 0, sizeof G); dfs2(1, 0); dp(1, 0); printf("%d\n", ans); } return 0; }
Problem J
最后这题完全读不进去啊……
题目大意就是给出一棵树,和若干条路径。
由于某些点不可经过,这些给出的路径是走不通的。
也就是说如果他给出了路径(u, v),那么u就不能走到v,反过来也一样。
求不可经过的点最少有几个。
这道题要用到一个结论:
如果树上两条路径有交集,
假设u1和u2分别为两条路径两个端点的LCA,
一定存在u1属于这个交集,或者u2属于这个交集。
对于路径(u, v),我们求出u和v的LCA——z。
然后对这p条路径,我们以deep[z]为关键字排序。
deep[z]越大,排越前面。
接下来就是一个贪心的过程。
我们从前往后处理这些路径,
如果当前这条路径的u或v已经被标记,那么直接跳过。
如果当前这条路径的u或v都没有被标记,
那么我们标记以z为根的子树的所有点。
然后对答案加1.
之前我们预处理出所有点的前序DFS序l[i]和后序DFS序r[i],
若要标记以i为根的子树的所有点,那么给区间l[i]到r[i]打上标记即可。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e4 + 10; const int A = 18; int deep[N], c[N], l[N], r[N], sz[N], son[N], top[N], father[N]; int n, m, ti, ans; vector <int> v[N]; struct node{ int x, y, z; friend bool operator < (const node &a, const node &b){ return deep[a.z] > deep[b.z]; } } path[N * 5]; void dfs(int x, int fa, int dep){ father[x] = fa; deep[x] = dep; l[x] = ++ti; sz[x] = 1; for (auto u : v[x]){ if (u == fa) continue; dfs(u, x, dep + 1); sz[x] += sz[u]; if (sz[son[x]] < sz[u]) son[x] = u; } r[x] = ti; } void dfs2(int x, int fa, int tp){ top[x] = tp; if (son[x]) dfs2(son[x], x, tp); for (auto u : v[x]){ if (u == son[x] || u == fa) continue; dfs2(u, x, u); } } int LCA(int x, int y){ for (; top[x] ^ top[y]; ){ if (deep[top[x]] < deep[top[y]]) swap(x, y); x = father[top[x]]; } return deep[x] > deep[y] ? y : x; } inline int update(int x, int y){ for (; x <= n; x += x & -x) ++c[x]; ++y; for (; y <= n; y += y & -y) --c[y]; } inline int query(int x){ int ret = 0; for (; x; x -= x & -x) ret += c[x]; return ret; } int main(){ while (~scanf("%d", &n)){ ++n; rep(i, 0, n + 1) v[i].clear(); ti = 0; rep(i, 2, n){ int x, y; scanf("%d%d", &x, &y); ++x; ++y; v[x].push_back(y); v[y].push_back(x); } memset(son, 0, sizeof son); dfs(1, 0, 0); dfs2(1, 0, 1); scanf("%d", &m); rep(i, 1, m){ int x, y, z; scanf("%d%d", &x, &y); ++x; ++y; z = LCA(x, y); path[i] = node{x, y, z}; } sort(path + 1, path + m + 1); memset(c, 0, sizeof c); ans = 0; rep(i, 1, m){ int x = query(l[path[i].x]), y = query(l[path[i].y]); if (x || y) continue; update(l[path[i].z], r[path[i].z]); ++ans; } printf("%d\n", ans); } return 0; }
Problem L
从第1个点扫过去,扫到第x1个点的时候游戏终止。
于是我们从x1+ 1接着扫,扫到游戏终止时的那个点x2
继续从x2 + 1开始扫。
周而复始,直到扫完为止。扫完一遍更新一次答案。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e6 + 10; int n, l, r, ret, ans; int a[N], b[N], c[N << 1], f[N << 1]; int main(){ while (~scanf("%d", &n)){ rep(i, 1, n) scanf("%d", a + i); rep(i, 1, n) scanf("%d", b + i); rep(i, 1, n) c[i] = a[i] - b[i]; rep(i, 1, n) c[i + n] = c[i]; rep(i, 1, n << 1) f[i] = f[i - 1] + c[i]; l = 1; r = 1; c[1] = 0; rep(i, 1, n) c[i] = c[i - 1] + a[i]; rep(i, 1, n) c[n + i] = c[n + i - 1] + a[i]; ret = 0; while (true){ while (r < 2 * n && r - l + 1 < n && f[r] - f[l - 1] >= 0) ++r; if (c[r] - c[l - 1] > ret){ ret = c[r] - c[l - 1]; ans = l - 1; } ++r; l = r; if (l > 2 * n) break; } printf("%d\n", ans); } return 0; }