A – 等差子序列
因为是个排列,读入时我们可以把已有的数值放在数轴上,发现只要有一个对称即可。我们可以用哈希的思想魔改一下树状数组,维护串和反串的哈希然后判断即可。
// FOJ1913.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 10100, bitnum = 133; typedef unsigned long long ull; int T, n, ai[MAX_N]; ull nodes[2][MAX_N], pw[MAX_N]; inline char nc() { static char buf[1000000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++; } int read() { int x = 0, f = 1; char ch = nc(); while (!isdigit(ch)) ch = nc(); while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = nc(); return x * f; } inline int lowbit(int x) { return x & (-x); } void update(ull *arr, int x) { for (int i = x; i <= n; i += lowbit(i)) arr[i] += pw[i - x]; } ull query(ull *arr, int x, ull ret = 0) { for (int i = x; i; i -= lowbit(i)) ret += arr[i] * pw[x - i]; return ret; } ull query(ull *arr, int l, int r) { return query(arr, r) - query(arr, l - 1) * pw[r - l + 1]; } int main() { T = read(); for (int i = pw[0] = 1; i < MAX_N; i++) pw[i] = pw[i - 1] * bitnum; while (T--) { memset(nodes, 0, sizeof(nodes)); n = read(); bool flag = false; for (int i = 1; i <= n; i++) { ai[i] = read(); int len = min(ai[i] - 1, n - ai[i]); if (len && query(nodes[0], ai[i] - len, ai[i]) != query(nodes[1], n - ai[i] - len + 1, n - ai[i] + 1)) flag = true; update(nodes[0], ai[i]), update(nodes[1], n - ai[i] + 1); } puts(flag ? "Y" : "N"); } return 0; }
B – 最短路
我们考虑一个常见套路:也就是把环缩起来变成一棵树,然后用树上差分的套路来做。当然,我们不能直接硬算,因为假设 \(LCA\) 为一个环点,那么统计环上的一段弧长就比较麻烦。我们考虑先求出从\(1\)号点的单源最段路,然后再统计每个环点的环长,可知环上两点\(A, B\)的距离为:\(\min \{ |pre[A] – pre[B]|, loop – |pre[A] – pre[B]| \} \)。其中,\(pre\)是新树上的距离,新树为原图暴力破环(且随机)后的图。
// FOJ1914.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e5 + 200; typedef pair<int, int> pii; typedef long long ll; int head[MAX_N], current, n, m, q, dfn[MAX_N], low[MAX_N], stk[MAX_N], tail, ptot; int ncnt, aff[MAX_N], up[15][MAX_N], dep[MAX_N]; ll dist[MAX_N], loops[MAX_N], prefix[MAX_N]; bool inst[MAX_N], vis[MAX_N]; vector<int> G[MAX_N]; struct edge { int to, nxt, weight; } edges[MAX_N << 1]; void addpath(int src, int dst, int weight) { edges[current].to = dst, edges[current].nxt = head[src]; edges[current].weight = weight, head[src] = current++; } void shortest_path(int src) { memset(dist, 0x3f, sizeof(dist)); priority_queue<pii> pq; pq.push(make_pair(0, src)), dist[src] = 0; while (!pq.empty()) { int u = pq.top().second; pq.pop(); if (vis[u]) continue; vis[u] = true; for (int i = head[u]; i != -1; i = edges[i].nxt) if (dist[edges[i].to] > dist[u] + edges[i].weight) dist[edges[i].to] = dist[u] + edges[i].weight, pq.push(make_pair(-dist[edges[i].to], edges[i].to)); } memset(vis, false, sizeof(vis)); } void tarjan(int u, int fa) { dfn[u] = ++ptot; for (int i = head[u]; i != -1; i = edges[i].nxt) if (edges[i].to != fa) { if (dfn[edges[i].to] == 0) up[0][edges[i].to] = u, prefix[edges[i].to] = prefix[u] + edges[i].weight, tarjan(edges[i].to, u); else if (dfn[edges[i].to] < dfn[u]) { loops[++ncnt] = 0LL + prefix[u] - prefix[edges[i].to] + edges[i].weight; for (int pt = u, pre; pt != edges[i].to;) G[edges[i].to].push_back(pt), aff[pt] = ncnt, pre = up[0][pt], up[0][pt] = edges[i].to, pt = pre; } } if (up[0][u] == fa) G[fa].push_back(u); } void dfs_collect(int u, int fa) { dep[u] = dep[fa] + 1; for (int i = 0, siz = G[u].size(); i < siz; i++) if (G[u][i] != fa) dfs_collect(G[u][i], u); } ll query(int x, int y) { if (dep[x] < dep[y]) swap(x, y); int u = x, v = y; for (int i = 14; i >= 0; i--) if (dep[up[i][x]] >= dep[y]) x = up[i][x]; if (x == y) return dist[u] - dist[v]; for (int i = 14; i >= 0; i--) if (up[i][x] != up[i][y]) x = up[i][x], y = up[i][y]; if (aff[x] && aff[x] == aff[y]) return dist[u] - dist[x] + dist[v] - dist[y] + min(llabs(prefix[x] - prefix[y]), loops[aff[x]] - llabs(prefix[x] - prefix[y])); return dist[u] + dist[v] - (dist[up[0][x]] << 1); } int main() { memset(head, -1, sizeof(head)); scanf("%d%d%d", &n, &m, &q); for (int i = 1, u, v, w; i <= m; i++) scanf("%d%d%d", &u, &v, &w), addpath(u, v, w), addpath(v, u, w); shortest_path(1), tarjan(1, 0); dfs_collect(1, 0); for (int i = 1; i <= 14; i++) for (int j = 1; j <= ptot; j++) up[i][j] = up[i - 1][up[i - 1][j]]; while (q--) { int x, y; scanf("%d%d", &x, &y); printf("%lld\n", query(x, y)); } return 0; }
C – 排斥反应
首先这个问题可以转换成在一个表格上做互不侵犯。具体转换的方式:左上角为起点,向左走视为\(+p\),向右走视为\(+q\),因为\(\gcd(p, q) = 1\),所以表格上的数字各不相同。转换为这个问题之后,我们就可以设计一个状压 DP 的方程。发现状态必须满足两个一不相邻且前端、末端的\(1\)不相邻,所以当\(p = 10\)时,状态变少为\(123\)种。可以考虑矩阵快速幂。
// FOJ1915.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 150, mod = 19921107; int p, q, stats[MAX_N], stat_tot; struct matrix { int mat[MAX_N][MAX_N]; void clear() { memset(mat, 0, sizeof(mat)); } int *operator[](const int &rhs) { return mat[rhs]; } matrix operator*(const matrix &rhs) { matrix ret; ret.clear(); for (int i = 1; i <= stat_tot; i++) for (int j = 1; j <= stat_tot; j++) for (int k = 1; k <= stat_tot; k++) ret[i][j] = (1LL * ret[i][j] + 1LL * mat[i][k] * rhs.mat[k][j] % mod) % mod; return ret; } matrix operator^(const int &rhs) { int tim = rhs; matrix ret, bas = *this; ret.clear(); for (int i = 1; i <= stat_tot; i++) ret[i][i] = 1; while (tim) { if (tim & 1) ret = ret * bas; bas = bas * bas; tim >>= 1; } return ret; } } A, trans; int main() { scanf("%d%d", &p, &q); // process the stats; for (int i = 0; i < (1 << p); i++) { bool flag = true; for (int j = 1; j < p; j++) if ((i & (1 << (j - 1))) && (i & (1 << j))) { flag = false; break; } if (p != 1) flag &= (!((i & 1) && (i & (1 << (p - 1))))); if (flag) stats[++stat_tot] = i; } if (q == 1) printf("%d\n", stat_tot), exit(0); int ans = 0; trans.clear(); for (int i = 1; i <= stat_tot; i++) { A[i][i] = 1; for (int j = 1; j <= stat_tot; j++) if (!(stats[i] & stats[j])) trans[i][j] = 1; } A = A * (trans ^ (q - 1)); for (int i = 1; i <= stat_tot; i++) for (int j = 1; j <= stat_tot; j++) if (!(stats[i] & stats[j])) ans = (1LL * ans + A[i][j]) % mod; printf("%d\n", ans); return 0; }