NOIP模拟测试14
以前的坑,填一下。
写在前面的:以后我的考试感想将主要写自己的反思,题解之类的就略写了,主要能让我自己回忆起来就行。
想看详细题解点这里 https://www.cnblogs.com/Gkeng,比我写的不知高到哪里去了。
Problem A:旋转子段
一对点反转后可以成为固定点,满足$a_i + i = a_j + j$.$a_i + i$可以确定一个反转区间的中心。
我们把每个$a_i + i$扔到桶里,这里用vector。枚举区间中心,按区间长度把每个桶内排序,扫一遍用区间内固定点个数更新答案。
这题没啥问题,实名谴责出题人假数据范围。
1 #include <bits/stdc++.h> 2 3 const int N = 1000005; 4 int n, ori[N << 2], ans, sum[N << 2], pos; 5 std::vector<int> v[N << 2]; 6 7 bool cmp(int x, int y) { 8 return abs(pos - (x << 1)) < abs(pos - (y << 1)); 9 } 10 11 signed main() { 12 scanf("%d", &n); 13 for (int i = 1; i <= n; i++) { 14 scanf("%d", &ori[i]); 15 sum[i] = sum[i - 1] + (ori[i] == i); 16 v[i + ori[i]].push_back(i); 17 } 18 for (int i = 2; i <= (n << 1); i++) { 19 if (v[i].empty()) continue; 20 pos = i; 21 std::sort(v[i].begin(), v[i].end(), cmp); 22 int j = 1; 23 for (auto k : v[i]) { 24 int l = k, r = i - k; 25 if (l > r) std::swap(l, r); 26 ans = std::max(ans, sum[l - 1] + sum[n] - sum[r] + j), j++; 27 } 28 } 29 return !printf("%d\n", ans); 30 }
Problem B:走格子
初看以为就是BFS,发现限制很多。
Portal可以直接等价于跑到离得最近的墙,建立Portal,就可以前往四个方向的任意一个墙。
想到暴力建图跑最短路。预处理墙的位置更好做一点。
本次考试以及OI生涯最大失误:把数据生成器错交到了OJ上。。。。。
我都不知道我的脑子里都在想点啥。。。。。。。。
以后提交之前必须check一下,100分白丢太痛苦,Rank 2 -> Rank 35
1 #include <bits/stdc++.h> 2 #define id(x, y) ((x - 1) * m + y) 3 4 const int N = 500005, M = 505; 5 int n, m, ecnt, head[N], st, ed, step[M][M], dis[N]; 6 int wall_l[M][M], wall_r[M][M], wall_u[M][M], wall_d[M][M]; 7 const int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1}; 8 char matrix[M][M]; 9 struct Edge {int to, nxt, val;} e[N << 3]; 10 std::queue<std::pair<int, int> > q; 11 bool vis[N]; 12 13 inline void addedge(int f, int to, int val) { 14 e[++ecnt] = {to, head[f], val}, head[f] = ecnt; 15 } 16 17 void link(int x, int y) { 18 if (matrix[x][y] != '.') return; 19 if (matrix[x][y + 1] == '.') addedge(id(x, y), id(x, y + 1), 1), 20 addedge(id(x, y + 1), id(x, y), 1);; 21 if (matrix[x + 1][y] == '.') addedge(id(x, y), id(x + 1, y), 1), 22 addedge(id(x + 1, y), id(x, y), 1); 23 if (wall_l[x][y] != id(x, y)) addedge(id(x, y), wall_l[x][y], step[x][y]); 24 if (wall_r[x][y] != id(x, y)) addedge(id(x, y), wall_r[x][y], step[x][y]); 25 if (wall_u[x][y] != id(x, y)) addedge(id(x, y), wall_u[x][y], step[x][y]); 26 if (wall_d[x][y] != id(x, y)) addedge(id(x, y), wall_d[x][y], step[x][y]); 27 } 28 29 void Dij() { 30 memset(dis, 0x3f, sizeof(dis)); 31 memset(vis, 0, sizeof(vis)); 32 std::priority_queue<std::pair<int, int> > que; 33 dis[st] = 0; 34 que.push(std::make_pair(0, st)); 35 while (!que.empty()) { 36 int x = que.top().second; 37 que.pop(); 38 if (vis[x]) continue; 39 vis[x] = 1; 40 for (int i = head[x], y = e[i].to; i; i = e[i].nxt, y = e[i].to) { 41 if (dis[y] > dis[x] + e[i].val) { 42 dis[y] = dis[x] + e[i].val; 43 if (!vis[y]) que.push(std::make_pair(-dis[y], y)); 44 } 45 } 46 } 47 } 48 49 signed main() { 50 scanf("%d%d", &n, &m); 51 for (int i = 1; i <= n; i++) { 52 scanf("%s", matrix[i] + 1); 53 for (int j = 1; j <= m; j++) { 54 if (matrix[i][j] == 'C') matrix[i][j] = '.', st = id(i, j); 55 else if (matrix[i][j] == 'F') matrix[i][j] = '.', ed = id(i, j); 56 else if (matrix[i][j] == '#') q.push(std::make_pair(i, j)); 57 } 58 } 59 60 while (!q.empty()) { 61 int x = q.front().first, y = q.front().second; 62 q.pop(); 63 for (int i = 0; i <= 3; i++) { 64 int xx = x + dx[i], yy = y + dy[i]; 65 if (matrix[xx][yy] == '.' && !step[xx][yy]) { 66 step[xx][yy] = step[x][y] + 1; 67 q.push(std::make_pair(xx, yy)); 68 } 69 } 70 } 71 72 for (int i = 1; i <= n; i++) { 73 for (int j = 1; j <= m; j++) { 74 if (matrix[i][j] != '.') continue; 75 if (matrix[i][j - 1] == '#') wall_l[i][j] = id(i, j); 76 else wall_l[i][j] = wall_l[i][j - 1]; 77 if (matrix[i - 1][j] == '#') wall_u[i][j] = id(i, j); 78 else wall_u[i][j] = wall_u[i - 1][j]; 79 } 80 } 81 82 for (int i = n; i >= 1; i--) { 83 for (int j = m; j >= 1; j--) { 84 if (matrix[i][j] != '.') continue; 85 if (matrix[i][j + 1] == '#') wall_r[i][j] = id(i, j); 86 else wall_r[i][j] = wall_r[i][j + 1]; 87 if (matrix[i + 1][j] == '#') wall_d[i][j] = id(i, j); 88 else wall_d[i][j] = wall_d[i + 1][j]; 89 } 90 } 91 92 for (int i = 1; i <= n; i++) 93 for (int j = 1; j <= m; j++) link(i, j); 94 95 Dij(); 96 if (dis[ed] == 0x3f3f3f3f) puts("no"); 97 else printf("%d\n", dis[ed]); 98 return 0; 99 }
Problem C:柱状图
$O(N^3)$暴力很好想,枚举位置,枚举高度,暴力计算,考场时间剩太少&贪正解,甚至这玩意都没打。
$O(N^2)$需要动动脑子:我们要得到一个等差数列,可以直接给每一项减掉一个等差数列,再把它填平。可以想到填到处理后的中位数是最优的。
它过不去。瓶颈在于答案的计算。可以去打那个树状数组的正解,把内层的N优化到log,我直接用退火优化掉了外层的N,优化到O(玄学)。
一些人对退火有争议,我无法理解。用退火也没有什么问题,它和三分二分一样都是优化复杂度的工具,不存在什么骗AC的事情。而且本题答案(你原来要二分的那个东西,不是退火退的那个东西)必定单谷,这势必保证了退火实际效率能够吊打正解。
爱说啥说啥吧,hhh。
1 #include <bits/stdc++.h> 2 #define ll long long 3 4 const double eps = 0.1; 5 const int N = 100005; 6 int n; 7 ll x[N], tmp[N], tmppos, nowpos, tmpans, nowans = 0x3f3f3f3f3f3f3f3f; 8 double T = 100000; 9 10 ll calc(int pos) { 11 ll mx = std::max(n - pos, pos - 1); 12 for (int i = 1; i <= n; i++) 13 tmp[i] = x[i] - mx + abs(i - pos); 14 int mid = (1 + n) >> 1; 15 ll ret = 0, mdzz = 0; 16 std::nth_element(tmp + 1, tmp + mid, tmp + n + 1); 17 mdzz = tmp[mid] > 0 ? tmp[mid] : 1; 18 for (int i = 1; i <= n; i++) 19 ret += abs(mdzz - tmp[i]); 20 return ret; 21 } 22 23 signed main() { 24 srand(clock() + time(0)); 25 scanf("%d", &n); 26 for (int i = 1; i <= n; i++) 27 scanf("%lld", &x[i]); 28 while (T > eps) { 29 tmppos = ((int) (nowpos + (int) (T * ((double) rand()/ (double) RAND_MAX) * (rand() % 2 ? 1 : -1)) % n + n)) % n + 1; 30 tmpans = calc(tmppos); 31 ll delta = tmpans - nowans; 32 if (delta < 0) 33 nowans = tmpans, nowpos = tmppos; 34 else if (exp((double) -delta / T) * RAND_MAX > rand()) 35 nowpos = tmppos; 36 T *= 0.976; 37 } 38 printf("%lld\n", nowans); 39 return 0; 40 }