「CSP-S 2022」假期计划的一个简单随机化做法

· · 个人记录

如果 A < B < C < D 就可以做到 O(n^2) DP。

当然肯定不能这样钦定,所以我们可以任意打乱编号顺序,然后跑一遍 DP。即,令 p = \{2, 3, 4, \ldots, n\},每次打乱数列 p,然后钦定 a' < b' < c' < d',取 A = p_{a'}, B = p_{b'}, C = p_{c'}, D = p_{d'}

所以一次的正确率是 $\frac 1{12}$。跑 $T$ 次的正确率就是 $$1 - \left( 1 - \frac 1{12} \right)^T$$ 有 $$1 - \left( 1 - \frac 1{12} \right)^{12} \approx 1 - \frac 1e \approx 63\%$$ 因为常数相当小,所以保守估计 2s 内可以跑 $100$ 次以上(在洛谷上甚至能跑两三百次)。 $$1 - \left( 1 - \frac 1{12} \right)^{100} \approx 99.98\%$$ ## Code ```cpp #include <cstdio> #include <algorithm> #include <cstdlib> #include <ctime> #define For(i, l, r) for (i = l; i <= r; i++) #define Fo(i, n) For(i, 1, n) #define Rof(i, r, l) for (i = r; i >= l; i--) #define Ro(i, n) Rof(i, n, 1) typedef long long int64; const int N = 2500 + 10; const int M = 20000 + 10; const int64 NINF = -5e18; int n, m, k; int64 ans = NINF; int pre[M], to[M], head[N], wcnt; int64 a[N]; bool w[N][N]; int id[N]; int64 f[N][4]; // f[i][j] 表示最后一个选的点是 i,已经选了 (j + 1) 个点的最优答案 inline void chkmax(int64 &x, int64 y) { x = x > y ? x : y; } inline void fix(int64 &x) { x = (x < 0) ? NINF : x; } inline void addedge(int u, int v) { pre[++wcnt] = head[u]; head[u] = wcnt; to[wcnt] = v; } inline void add2edge(int u, int v) { addedge(u, v); addedge(v, u); } void bfs(int s) { static int dis[N]; static int q[N], ql, qr; int i, u; Fo(i, n) dis[i] = -1; dis[s] = 0; q[ql = qr = 1] = s; while (ql <= qr) { u = q[ql++]; for (i = head[u]; i; i = pre[i]) if (dis[to[i]] == -1) { dis[to[i]] = dis[u] + 1; if (dis[to[i]] < k) q[++qr] = to[i]; } } Fo(i, n) w[s][i] = (dis[i] >= 0); } inline int randint(int l, int r) { return rand() % (r - l + 1) + l; } inline void random_shuffle(int *a, int n) { int i; For(i, 2, n) std::swap(a[randint(1, i)], a[i]); } void test() { int i, j; bool *vis; Fo(i, n) id[i] = i; random_shuffle(id + 1, n - 1); For(i, 2, n) { f[i][0] = 0; f[i][1] = f[i][2] = f[i][3] = NINF; vis = w[id[i]]; Rof(j, i - 1, 2) if (vis[id[j]]) { chkmax(f[i][1], f[j][0]); chkmax(f[i][2], f[j][1]); chkmax(f[i][3], f[j][2]); } f[i][0] += a[id[i]]; f[i][1] += a[id[i]]; f[i][2] += a[id[i]]; f[i][3] += a[id[i]]; if (!vis[1]) f[i][0] = f[i][3] = NINF; fix(f[i][0]); fix(f[i][1]); fix(f[i][2]); fix(f[i][3]); ans = std::max(ans, f[i][3]); } } int main() { int i, u, v; int start = clock(); srand(time(NULL)); scanf("%d %d %d", &n, &m, &k); k++; For(i, 2, n) scanf("%lld", &a[i]); Fo(i, m) { scanf("%d %d", &u, &v); add2edge(u, v); } Fo(i, n) bfs(i); Fo(i, 1000) { test(); if (double(clock() - start) / CLOCKS_PER_SEC >= 1.8) break; } printf("%lld\n", ans); return 0; } ```