可能是史上最弱的验题人——
Problem A
(小)模拟。
#include <bits/stdc++.h> using namespace std; int T; int main(){ scanf("%d", &T); while (T--){ char rk[10]; scanf("%s", rk); int n = strlen(rk); for (int i = 0; i < 3 - n; ++i) putchar(' '); printf("%s", rk); putchar('|'); char S[20]; scanf("%s", S); printf("%s", S); n = strlen(S); for (int i = n; i < 16; ++i) putchar(' '); putchar('|'); scanf("%s", rk); printf("%s", rk); scanf("%s", rk); if (rk[0] == 'R' && rk[1] == 'u') { int x; scanf("%d", &x); putchar('|'); putchar('['); for (int i = 1; i <= x; ++i) putchar('X'); for (int i = 1; i <= 10 - x; ++i) putchar(' '); putchar(']'); } else{ if (rk[0] == 'F') { rk[0] = 'A'; rk[1] = 'C'; rk[2] = '*'; rk[3] = ' '; } putchar('|'); putchar('['); for (int i = 0; i < 4; ++i) putchar(' '); printf("%s", rk); int n = strlen(rk); for (int i = 1; i <= 6 - n; ++i) putchar(' '); putchar(']'); } puts(""); } }
Problem B
观察到$d$大于$316$的质因子最多只有一个,那么先判掉$<= 316$的所有质因子,搞个前缀和就可以了。
然后特判大于$316$的质因子即可,方法有很多。
#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 fi first #define se second #define MP make_pair typedef long long LL; typedef pair <int, int> PII; const int N = 1e5 + 10; int T; int s[70][N]; int a[N]; int n, m; int re[N]; int c[N]; int id = 0; int prime[N], fp[N], val[N]; vector <PII> pri[N]; vector <int> v[N]; int main(){ rep(i, 2, 1e5){ for (int j = i + i; j <= 1e5; j += i) c[j] = 1; } rep(i, 2, 1e5){ if (!c[i]){ ++id; prime[id] = i; fp[i] = id; } } rep(i, 1, 1e5) val[i] = i; rep(i, 2, 1e5) if (!c[i]){ for (int j = i + i; j <= 1e5; j += i){ int cnt = 0; while (val[j] % i == 0) val[j] /= i, ++cnt; pri[j].push_back(MP(i, cnt)); if (i > 316) re[j] = i; } } rep(i, 2, 1e5) if (val[i] > 1){ pri[i].push_back(MP(i, 1)); if (val[i] > 316) re[i] = val[i]; } scanf("%d", &T); while (T--){ scanf("%d%d", &n, &m); memset(s, 0, sizeof s); memset(a, 0, sizeof a); rep(i, 1, 1e5 + 1) v[i].clear(); rep(i, 1, n){ int x; scanf("%d", &x); for (auto u : pri[x]){ int nowid = fp[u.fi], nowcnt = u.se; if (u.fi <= 316){ s[nowid][i] += nowcnt; } } if (re[x]){ a[i] = re[x]; v[re[x]].push_back(i); } } rep(i, 1, 67){ rep(j, 1, n) s[i][j] += s[i][j - 1]; } while (m--){ int x, y, z; scanf("%d%d%d", &x, &y, &z); int ret = 1; for (auto u : pri[z]){ if (u.fi > 316) break; int nowid = fp[u.fi], nowcnt = u.se; ret &= (s[nowid][y] - s[nowid][x - 1] >= u.se); } if (re[z]){ int num = re[z]; int sz = (int)v[num].size(); if (sz == 0) ret = 0; else{ int l = 0, r = sz - 1; if (v[num][r] < x){ ret = 0; } else{ while (l + 1 < r){ int mid = (l + r) >> 1; if (v[num][mid] >= x){ r = mid; } else{ l = mid + 1; } } int t; if (v[num][l] >= x) t = l; else t = r; int minpos = v[num][t]; ret &= (minpos <= y); } } } puts(ret ? "Yes" : "No"); } } return 0; }
Problem C
二分一下就可以了,具体实现的时候要尽量避开log函数,否则会因为精度问题产生误差。
#include <bits/stdc++.h> using namespace std; #define ll long long ll LOG(ll x){ ll ans = 0; while (true) { if (x == 1) return ans; else if (x == 2) return ans + 1; x = x - (x / 2); ans++; } } bool check(ll n, ll k, int a, int b) { ll m = LOG(n); if (m == 0) return 1; ll tmp = 1; while (a--) { if (k / tmp < n) return 0; tmp *= n; } while (b--) { if (k / tmp < m) return 0; tmp *= m; } return 1; } int a, b; int T; ll k; int main() { scanf("%d", &T); while (T--) { scanf("%d%d%I64d", &a, &b, &k); ll l = 1, r = k, ans; while (l <= r) { ll mid = (l + r) / 2; if (check(mid, k, a, b)) ans = mid, l = mid + 1; else r = mid - 1; } printf("%I64d ", ans); } return 0; }
Problem D
难题。考虑DP,设$f[i][j][x][y]$表示走到第$i$行第$j$列,有$x$个路径上本来应该计入答案的格子没有计入答案,
有$y$个本不属于这条路径的格子计入了答案的最大值。
走到$a_{i,j}$的时候把那些肯定不可能走到的格子排序然后贪心转移即可。
#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 = 53; const int K = 27; int T; int f[N][N][K][K], a[N][N], c[N]; int n, m, k; int ans = 0; inline void up(int &x, int y){ if (x < y) x = y; } int main(){ scanf("%d", &T); while (T--){ scanf("%d%d%d", &n, &m, &k); rep(i, 1, n){ rep(j, 1, m) scanf("%d", a[i] + j); } memset(f, 0xc0, sizeof f); f[1][1][0][0] = a[1][1]; f[1][1][0][1] = 0; rep(i, 1, n){ rep(j, 1, m){ rep(q, 1, m){ if (q != j){ if (q < j){ c[q] = a[i + 1][q]; } else{ c[q] = a[i][q]; } } else{ c[q] = 0; } } sort(c + 1, c + m + 1, greater<int>()); rep(q, 1, m){ c[q] += c[q - 1]; } rep(p, 0, k){ rep(q, 0, k){ up(f[i][j + 1][p][q], f[i][j][p][q] + a[i][j + 1]); up(f[i][j + 1][p][q + 1], f[i][j][p][q]); rep(z, 0, m - 1){ if (p + z > k) break; up(f[i + 1][j][p + z][q], f[i][j][p][q] + a[i + 1][j] + c[z]); up(f[i + 1][j][p + z][q + 1], f[i][j][p][q] + c[z]); } } } } } ans = 0; rep(i, 0, k) up(ans, f[n][m][i][i]); printf("%d ", ans); } return 0; }
Problem E
直接上最短路就可以了,注意计算对数要用整数运算,跑最短路要堆优化Dij
(不是这个算法的全被卡了)
#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 = 2e5 + 10; struct node{ int u; LL w; friend bool operator < (const node &a, const node &b){ return a.w > b.w; } }; int T; int b[N]; int inqueue[N]; int n, m; int ret; LL d[N]; LL a[N]; vector <PII> v[N]; void dij(){ priority_queue <node> q; static bool vis[N]; rep(i, 1, n + 1) d[i] = 4e18, vis[i] = false; q.push({1, 1}); d[1] = 1; while (!q.empty()){ int u = q.top().u; q.pop(); if (vis[u]) continue; vis[u] = 1; for (auto edge : v[u]){ int xx = edge.fi, id = edge.se, wb = b[id]; LL wa = a[id]; if (d[u] + wa < d[xx] && (d[u] + wa) / d[u] >= (1ll << wb)){ d[xx] = d[u] + wa; q.push({xx, d[xx]}); } } } } int main(){ scanf("%d", &T); while (T--){ rep(i, 0, n + 1) v[i].clear(); scanf("%d%d", &n, &m); rep(i, 1, m){ int x, y; scanf("%d%d%lld%d", &x, &y, a + i, b + i); v[x].push_back(MP(y, i)); } dij(); if (d[n] == 4e18){ puts("-1"); continue; } ret = 0; while (d[n]){ ++ret; d[n] /= 2ll; } printf("%d ", ret - 1); } return 0; }
Problem F
勇敢地直接上树上莫队,对$01$数组分块,同时维护每个块内$0$的个数,然后注意一些细节,就可以AC了。
在HDOJ上大概要跑$10s$。
二分答案+主席树方法待补。
#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 = 2e5 + 10; const int A = 19; int T; int n, m; int cnt, bs, g, top, ti, ret; int a[N], b[N], c[N], bg[N]; int f[N][A], deep[N], dfn[N]; int belong[N], stk[N]; int vis[N], s[N], ans[N]; int L[N], R[N]; int bl[N], bss, blocknum; int extra; int exist[N]; vector <int> v[N]; struct node{ int x, y, id; friend bool operator < (const node &a, const node &b){ return belong[a.x] == belong[b.x] ? dfn[a.y] < dfn[b.y] : belong[a.x] < belong[b.x]; } } q[N]; int LCA(int a, int b){ if (deep[a] < deep[b]) swap(a, b); for (int i = 0, delta = deep[a] - deep[b]; delta; delta >>= 1, ++i) if (delta & 1) a = f[a][i]; if (a == b) return a; dec(i, 18, 0) if (f[a][i] ^ f[b][i]){ a = f[a][i]; b = f[b][i]; } return f[a][0]; } void dfs(int x, int fa, int dep){ deep[x] = dep; if (fa){ f[x][0] = fa; for (int i = 0; f[f[x][i]][i]; ++i) f[x][i + 1] = f[f[x][i]][i]; } for (auto u : v[x]){ if (u == fa) continue; dfs(u, x, dep + 1); } } void dfs(int x, int fa){ dfn[x] = ++ti; int bot = top; for (auto u : v[x]){ if (u == fa) continue; dfs(u, x); if (top - bot >= bs){ ++g; while (top ^ bot) belong[stk[top--]] = g; } } stk[++top] = x; } void rev(int x){ if (c[a[x]] == 0) --bg[bl[a[x]]]; else ++bg[bl[a[x]]]; c[a[x]] ^= 1; vis[x] ^= 1; } void work(int x, int y){ for (; x ^ y; ){ if (deep[x] < deep[y]) swap(x, y); rev(x); x = f[x][0]; } } void init(){ rep(i, 0, n + 1) v[i].clear(); memset(c, 0, sizeof c); memset(f, 0, sizeof f); memset(vis, 0, sizeof vis); memset(exist, 0, sizeof exist); ti = 0; top = 0; g = 0; cnt = 0; bs = 0; ret = 0; } int main(){ scanf("%d", &T); while (T--){ scanf("%d%d", &n, &m); init(); rep(i, 1, n) scanf("%d", a + i), b[i] = a[i]; rep(i, 1, n) exist[a[i]] = 1; rep(i, 1, 200002) if (!exist[i]){ extra = i; break; } sort(b + 1, b + n + 1); cnt = unique(b + 1, b + n + 1) - b - 1; rep(i, 1, n) a[i] = lower_bound(b + 1, b + cnt + 1, a[i]) - b; rep(i, 2, n){ int x, y; scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); } dfs(1, 0, 0); bs = sqrt(n); bss = sqrt(cnt); dfs(1, 0); rep(i, 1, cnt) bl[i] = (i - 1) / bss + 1; blocknum = bl[cnt]; rep(i, 1, blocknum) L[i] = 1e9, R[i] = 0; rep(i, 1, cnt){ L[bl[i]] = min(L[bl[i]], i); R[bl[i]] = max(R[bl[i]], i); } rep(i, 1, blocknum) bg[i] = R[i] - L[i] + 1; rep(i, 1, m){ scanf("%d%d", &q[i].x, &q[i].y); q[i].id = i; if (dfn[q[i].x] > dfn[q[i].y]) swap(q[i].x, q[i].y); } sort(q + 1, q + m + 1); q[0].x = q[0].y = 1; rep(i, 1, m){ work(q[i - 1].x, q[i].x); work(q[i - 1].y, q[i].y); int lca = LCA(q[i].x, q[i].y); rev(lca); int now = 0, fg = 0, ret = 200001; rep(i, 1, blocknum){ now += bg[i]; if (now > 0){ fg = 1; rep(j, L[i], R[i]) if (!c[j]){ ret = j; break; } break; } } if (fg) ans[q[i].id] = min(b[ret], extra); else ans[q[i].id] = extra; rev(lca); } rep(i, 1, m) printf("%d ", ans[i]); } return 0; }
Problem G
全场题。
#include <bits/stdc++.h> using namespace std; const int N = 505, INF = 0x3f3f3f3f; int t; int main(){ scanf("%d",&t); for (int id=1001;id<=1000+t;id++){ int n,m; scanf("%d%d",&n,&m); int ans1=INF,ans2=INF; for (int i=1;i<=n;i++) { int x; scanf("%d",&x); ans1=min(ans1,x); } for (int i=1;i<=m;i++) { int x; scanf("%d",&x); ans2=min(ans2,x); } printf("Problem %d: ",id); printf("Shortest judge solution: %d bytes. ",ans1); if (ans2==INF) printf("Shortest team solution: N/A bytes. "); else printf("Shortest team solution: %d bytes. ",ans2); } }
Problem H
难题,题目给定的是一个二分图模型,
我们需要把这个二分图的模型转化成基环生成树森林。
然后从高位到低位分治,把当前集合根据最高位$0$或$1$分成两个不同的集合。
显然集合内部连边方案更优。可以证明当两个集合大小都超过$3$时,没有横跨集合的边。
#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 = 3e5 + 10; int T; int n; LL solve(int d, vector<int> a){ if (d < 0) return 0; int n = (int)a.size(); if (n <= 4){ LL ret = 0; vector <int> c; rep(i, 0, n - 1) rep(j, i + 1, n - 1) c.push_back(a[i] ^ a[j]); sort(c.begin(), c.end()); for (int i = 0; i < n && i < n * (n - 1) / 2; i++) ret += c[i]; return ret; } vector <int> b[2]; rep(i, 0, n - 1) b[a[i] >> d & 1].push_back(a[i]); a.clear(); LL ret = solve(d - 1, b[0]) + solve(d - 1, b[1]); int t[2] = {(int)b[0].size(), (int)b[1].size()}; if (t[0] && t[1] && (t[0] < 3 || t[1] < 3)){ int mi = 2e9; rep(i, 0, t[0] - 1) rep(j, 0, t[1] - 1) mi = min(mi, b[0][i] ^ b[1][j]); ret += mi; } return ret; } int main(){ scanf("%d", &T); while (T--){ scanf("%d", &n); vector <int> a(n); rep(i, 0, n - 1) scanf("%d", &a[i]); printf("%lld ", solve(29, a)); } return 0; }
Problem 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) #define MP make_pair #define fi first #define se second typedef long long LL; const int N = 1e6 + 10; int T; int n; char s[N], ans[N]; int main(){ scanf("%d", &T); while (T--){ scanf("%d", &n); scanf("%s", s + 1); ans[n] = '>'; dec(i, n - 1, 1){ if (s[i] < s[i + 1]) ans[i] = '<'; else if (s[i] > s[i + 1]) ans[i] = '>'; else ans[i] = ans[i + 1]; } ans[n] = ' '; puts(ans + 1); } return 0; }
Problem J
由于数据是随机的,所以直接枚举回文重心往两边不断延伸(也就是直接暴力)就可以了。
VP的时候直接尝试只枚举长度 $<= 3$的回文串,直接AC了。
赛前预计AC $≈$ $30$,赛场上去掉打星队AC $= 3$
……
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100005; vector<int>link[N]; int a[N]; int cnt[N]; int t; int main(){ scanf("%d",&t); while(t--) { int n; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); ll ans=n; for (int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); link[x].push_back(y); link[y].push_back(x); if (a[x]==a[y]) ans++; } for (int i=1;i<=n;i++) { for (int v:link[i]) { ans+=cnt[a[v]]; cnt[a[v]]++; } for (int v:link[i]) cnt[a[v]]--; } printf("%lld ",ans); for (int i=1;i<=n;i++) link[i].clear(); } }
Problem K
难题。考虑容斥。
计数的时候对于某个格子,如何减掉重复计算的答案?
把这个矩阵往左,往上,往左上各推一个单位,就可以很巧妙地容斥了。
#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 = 100005; const int M = 1005; inline LL calc(int n){ return 1ll * n * (n - 1) * (n - 2) / 6;} int T; int n; int x[N][2], y[N][2]; int c[M][M]; LL ret[4]; int main(){ scanf("%d", &T); while (T--){ scanf("%d", &n); rep(i, 1, n) rep(j, 0, 1) scanf("%d%d", x[i] + j, y[i] + j); memset(ret, 0, sizeof ret); rep(op, 0, 3){ memset(c, 0, sizeof c); rep(i, 1, n){ int sx = -(op / 2), sy = -(op % 2); c[x[i][0]][y[i][0]]++; c[x[i][0]][y[i][1] + sy + 1]--; c[x[i][1] + sx + 1][y[i][0]]--; c[x[i][1] + sx + 1][y[i][1] + sy + 1]++; } rep(i, 1, M - 1){ rep(j, 1, M - 1){ c[i][j] += c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1]; ret[op] += calc(c[i][j]); } } } printf("%lld ", ret[0] - ret[1] - ret[2] + ret[3]); } return 0; }