【比赛链接】
- 点击打开连接
【题解链接】
- 点击打开链接
**【A】**A Serial Killer
【思路要点】
- 维护两个字符串模拟。
- 时间复杂度 O ( N ) O(N) O(N)。
【代码】
#include
using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } string a, b, c; int main() { cin >> a >> b; int n; cin >> n; cout << a << ' ' << b << endl; for (int i = 1; i <= n; i++) { cin >> c; if (a == c) cin >> a; else cin >> b; cout << a << ' ' << b << endl; } return 0; }
**【B】**Sherlock and his girlfriend
【思路要点】
- 当 N ≤ 2 N≤2 N≤2,将所有数涂为一种颜色。
- 否则,将质数涂为一种颜色,非质数涂为一种颜色。
- 时间复杂度 O ( N N ) O(N\sqrt{N}) O(NN)。
【代码】
#include
using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } bool prime(int x) { for (int i = 2; i * i <= x; i++) if (x % i == 0) return false; return true; } int main() { int n; read(n); if (n <= 2) { printf("%d\n", 1); for (int i = 1; i <= n; i++) printf("%d ", 1); return 0; } printf("%d\n", 2); for (int i = 1; i <= n; i++) printf("%d ", 1 + prime(i + 1)); return 0; }
**【C】**Molly’s Chemicals
【思路要点】
- 枚举可能的序列和(至多 O ( L o g K ) O(LogK) O(LogK)种)。
- 枚举右端点,维护一个以前缀和为下标的左端点map,查询对应合法的左端点的个数即可。
- 时间复杂度 O ( N L o g N L o g V ) O(NLogNLogV) O(NLogNLogV)。
【代码】
#include
using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, a[MAXN]; long long ans, k, s[MAXN]; long long calc(long long x) { static map <long long, int> mp; long long ans = 0; mp.clear(); for (int i = 1; i <= n; i++) { mp[s[i - 1]]++; ans += mp[s[i] - x]; } return ans; } int main() { read(n), read(k); for (int i = 1; i <= n; i++) read(a[i]), s[i] = s[i - 1] + a[i]; long long now = 1; while (abs(now) <= 1e15) { ans += calc(now); now = now * k; if (now == 1) break; } writeln(ans); return 0; }
**【D】**The Door Problem
【思路要点】
- 考虑每一扇门,若其原本处于开启状态,其对应的两个开关应该开闭状态相同;否则其对应的两个开关应该开闭状态不同。
- 将开关的开闭看作变量,容易得到一个2-SAT的解法。
- 注意到本题中所建出的图为无向图,我们只需要用并查集简单维护即可。
- 时间复杂度 O ( N + M ) O(N+M) O(N+M)。
【代码】
#include
using namespace std; const int MAXN = 2e5 + 5; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, f[MAXN], r[MAXN], point[MAXN][2]; vector <int> p[MAXN]; int F(int x) { if (f[x] == x) return x; else return f[x] = F(f[x]); } int main() { read(n), read(m); for (int i = 1; i <= n; i++) read(r[i]); for (int i = 1; i <= m; i++) { int x; read(x); for (int j = 1; j <= x; j++) { int pos; read(pos); p[pos].push_back(i); } } int tot = 0; for (int i = 1; i <= m; i++) { point[i][0] = ++tot; point[i][1] = ++tot; } for (int i = 1; i <= tot; i++) f[i] = i; for (int i = 1; i <= n; i++) { int x = p[i][0]; int y = p[i][1]; if (r[i] == 0) { f[F(point[x][0])] = F(point[y][1]); f[F(point[x][1])] = F(point[y][0]); } else { f[F(point[x][0])] = F(point[y][0]); f[F(point[x][1])] = F(point[y][1]); } } for (int i = 1; i <= m; i++) if (F(point[i][0]) == F(point[i][1])) { printf("NO\n"); return 0; } printf("YES\n"); return 0; }
**【E】**The Holmes Children
【思路要点】
- 不难发现 f = ϕ , g = 1 ∗ f = i d f=\phi,g=1*f=id f=ϕ,g=1∗f=id。
- 题目本质上是对 N N N求了 ⌊ K + 1 2 ⌋ \lfloor\frac{K+1}{2}\rfloor ⌊2K+1⌋次欧拉函数。
- 注意到奇数求欧拉函数后会变为偶数,而偶数求欧拉函数后会减半,所以 N N N在求 O ( L o g N ) O(LogN) O(LogN)次欧拉函数后就会变成1。
- 变成1后终止该过程即可。
- 注意输出时对 1 0 9 + 7 10^9+7 109+7取模。
- 时间复杂度 O ( N L o g N ) O(\sqrt{N}LogN) O(NLogN)。
【代码】
#include
using namespace std; const int MAXN = 100005; const int P = 1e9 + 7; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } long long phi(long long x) { long long ans = x; for (int i = 2; 1ll * i * i <= x; i++) { if (x % i == 0) { ans = ans / i * (i - 1); while (x % i == 0) x /= i; } } if (x != 1) ans = ans / x * (x - 1); return ans; } int main() { long long n, k; read(n), read(k); if (k % 2 == 1) k = (k + 1) / 2; else k /= 2; while (k--) { n = phi(n); if (n == 1) break; } writeln(n % P); return 0; }
**【F】**Sherlock’s bet to Moriarty
【思路要点】
- 显然如果将题目中的多边形转成对偶图,那么会得到一棵树。
- 对这棵树进行树分治,每次在分治重心上标上其在点分树上的深度即是一组符合条件的解。
- 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)。
- 关于实现上的一些细节:
- 我们当然可以使用传统的平面图转对偶图的方法来解题,但我们也可以利用本题的特殊性得到一个 编程复杂度更低的做法:对所有边按照两侧较少的点的数量排序,按少到多的顺序加入多边形,显然每加入一条边都会形成一个不可分割的多边形,也就是一个节点。维护另外一侧仍要被继续分割的多边形的节点顺序即可。
- 关于对多边形进行标号,我们只需要对每个多边形用vector维护一个降序的顶点集合,利用vector的字典序比较即可对多边形进行排序,从而标号。注意两个多边形至多会有两个公共点,因此字典序比较的复杂度是 O ( 1 ) O(1) O(1)的。
【代码】
#include
using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct edge {int x, y, val; } e[MAXN]; int n, m, tot, num[MAXN], ans[MAXN]; int root, weight[MAXN], size[MAXN]; int nxt[MAXN], home[MAXN]; bool vis[MAXN]; vector <int> a[MAXN], nodes[MAXN]; map <int, int> behind[MAXN]; bool cmp(edge x, edge y) { return x.val < y.val; } bool cnp(int x, int y) { return nodes[x] < nodes[y]; } void getroot(int pos, int fa, int tot) { size[pos] = 1; weight[pos] = 0; for (unsigned i = 0; i < a[pos].size(); i++) if (a[pos][i] != fa && !vis[a[pos][i]]) { getroot(a[pos][i], pos, tot); size[pos] += size[a[pos][i]]; chkmax(weight[pos], size[a[pos][i]]); } chkmax(weight[pos], tot - size[pos]); if (weight[pos] < weight[root]) root = pos; } void calcsize(int pos, int fa) { size[pos] = 1; for (unsigned i = 0; i < a[pos].size(); i++) if (a[pos][i] != fa && !vis[a[pos][i]]) { calcsize(a[pos][i], pos); size[pos] += size[a[pos][i]]; } } void work(int pos, int depth) { vis[pos] = true; ans[num[pos]] = depth; calcsize(pos, 0); for (unsigned i = 0; i < a[pos].size(); i++) if (!vis[a[pos][i]]) { root = 0; getroot(a[pos][i], pos, size[a[pos][i]]); work(root, depth + 1); } } void debug(int pos) { printf("%d:\n", pos); printf(" Vertex:"); for (unsigned i = 0; i < nodes[pos].size(); i++) printf(" %d", nodes[pos][i]); printf("\n"); printf(" Edge:"); for (unsigned i = 0; i < a[pos].size(); i++) printf(" %d", a[pos][i]); printf("\n"); printf(" Number: %d\n", num[pos]); } int main() { read(n), read(m); for (int i = 1; i <= m; i++) { read(e[i].x), read(e[i].y); if (e[i].x > e[i].y) swap(e[i].x, e[i].y); e[i].val = e[i].y - e[i].x - 1; chkmin(e[i].val, n - 2 - e[i].val); } sort(e + 1, e + m + 1, cmp); for (int i = 1; i <= n; i++) if (i == n) nxt[n] = 1; else nxt[i] = i + 1; for (int i = 1; i <= m; i++) { int now = ++tot, s = e[i].y, t = e[i].x; if (e[i].y - e[i].x - 1 == e[i].val) swap(s, t); nodes[now].push_back(s); nodes[now].push_back(t); int p = s; while (nxt[s] != t) { int q = nxt[s]; nodes[now].push_back(q); if (behind[p][q] != 0) { int tmp = behind[p][q]; a[tmp].push_back(now); a[now].push_back(tmp); } nxt[s] = nxt[p = q]; } if (behind[p][t] != 0) { int tmp = behind[p][t]; a[tmp].push_back(now); a[now].push_back(tmp); } behind[s][t] = now; if (i != m) continue; now = ++tot; swap(s, t); a[now].push_back(now - 1); a[now - 1].push_back(now); nodes[now].push_back(s); nodes[now].push_back(t); p = s; while (nxt[s] != t) { int q = nxt[s]; nodes[now].push_back(q); if (behind[p][q] != 0) { int tmp = behind[p][q]; a[tmp].push_back(now); a[now].push_back(tmp); } nxt[s] = nxt[p = q]; } if (behind[p][t] != 0) { int tmp = behind[p][t]; a[tmp].push_back(now); a[now].push_back(tmp); } behind[s][t] = now; } if (m == 0) tot++; for (int i = 1; i <= tot; i++) { sort(nodes[i].begin(), nodes[i].end()); reverse(nodes[i].begin(), nodes[i].end()); home[i] = i; } sort(home + 1, home + tot + 1, cnp); for (int i = 1; i <= tot; i++) num[home[i]] = i; weight[root = 0] = n + 1; getroot(1, 0, n); work(root, 1); for (int i = 1; i <= tot; i++) printf("%d ", ans[i]); return 0; }
**【G】**Sherlock and the Encrypted Data
【思路要点】
- 我们发现数值是否减小仅和原数中出现的最大的十六进制数位有关。
- 数位DP,记 f i , j , s f_{i,j,s} fi,j,s表示当前完成了最高的 i i i位,已经不再紧贴上界,当前出现的最大十六进制数位为 j j j,最后16位已经出现的数值为 s s s。
- f i , j , s f_{i,j,s} fi,j,s可以在 O ( 2 16 ∗ 1 6 3 ) O(2^{16}*16^3) O(216∗163)的时间内通过简单数位DP求出。
- 对于每个询问,我们只需要额外对于紧贴上界部分进行计算即可。
- 时间复杂度 O ( 2 16 ∗ 1 6 3 + Q ∗ 1 6 2 ) O(2^{16}*16^3+Q*16^2) O(216∗163+Q∗162)。
【代码】
#include
using namespace std; const int MAXN = 17; const int MAXS = 1 << 16; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int a[MAXN]; char s[MAXN]; long long dp[MAXN][MAXN][MAXS]; long long work(bool flg, int pos, int Max, int s) { if (pos == 0) { return ((1 << Max) & s) != 0; } if (flg) { long long ans = 0; for (int i = 0; i <= a[pos]; i++) if (pos <= 4) ans += work(i == a[pos], pos - 1, max(Max, i), s ^ (i << (pos - 1) * 4)); else ans += work(i == a[pos], pos - 1, max(Max, i), s); return ans; } else { if (dp[pos][Max][s] != -1) return dp[pos][Max][s]; long long ans = 0; for (int i = 0; i <= 15; i++) if (pos <= 4) ans += work(false, pos - 1, max(Max, i), s ^ (i << (pos - 1) * 4)); else ans += work(false, pos - 1, max(Max, i), s); return dp[pos][Max][s] = ans; } } long long solve() { return work(true, 16, 0, 0); } int main() { int T; read(T); memset(dp, -1, sizeof(dp)); while (T--) { scanf("%s", s + 1); int len = strlen(s + 1); memset(a, 0, sizeof(a)); for (int i = 1; i <= len; i++) { int now = s[i] - '0'; if (s[i] >= 'a') now = s[i] - 'a' + 10; a[len - i + 1] = now; } if (len == 1 && a[1] == 0) a[1]++; for (int i = 1; true; i++) if (a[i]) { a[i]--; break; } else a[i] = 15; long long ans = -solve(); scanf("%s", s + 1); len = strlen(s + 1); memset(a, 0, sizeof(a)); for (int i = 1; i <= len; i++) { int now = s[i] - '0'; if (s[i] >= 'a') now = s[i] - 'a' + 10; a[len - i + 1] = now; } ans += solve(); writeln(ans); } return 0; }