[关闭]
@dxbdly 2022-10-04T12:02:19.000000Z 字数 6097 阅读 212

10.4模拟赛 —— Violet I

2022秋


Contest_Summray

期望得分

T1 T2 T3 Sum
100 100 10 210

实际得分:

T1 T2 T3 Sum Rank
90 100 0 190 3

T1 Maze

Description

给你一个 的地图,上下行走需要花费 时间,左右行走需要花费 时间。求出最大的 ,使得从起点到终点的最短时间小于等于 。保留 位小数。

Solution

签到题。

答案显然单调。

二分 ,最短路算出时间,与 比较即可。

Code

  1. //The Code Is From Dawn
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. inline int read() {
  5. int x = 0;
  6. char c = getchar();
  7. bool f = 0;
  8. while(!isdigit(c)) f |= (c == '-'), c = getchar();
  9. while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();
  10. return f ? -x : x;
  11. }
  12. const int maxn = 205, INF = 1e9 + 7;
  13. const double eps = 1e-7;
  14. int T;
  15. double X, Dist[maxn][maxn];
  16. int n, m;
  17. int a[maxn][maxn], sx, sy, ex, ey;
  18. int go[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, Vst[maxn][maxn];
  19. inline double Dijstra(double V) {
  20. priority_queue < pair<double, pair<int, int> > > Q;
  21. Q.push(make_pair(0.0, make_pair(sx, sy))), Dist[sx][sy] = 0.0;
  22. while(!Q.empty()) {
  23. int x = Q.top().second.first, y = Q.top().second.second;
  24. Q.pop();
  25. if(Vst[x][y]) continue;
  26. Vst[x][y] = 1;
  27. if(x == ex && y == ey) { return Dist[x][y]; }
  28. for(register int i = 0; i < 4; ++i) {
  29. int nx = x + go[i][0], ny = y + go[i][1];
  30. if(nx < 1 || ny < 1 || nx > n || ny > m || Vst[nx][ny] || !a[nx][ny]) continue;
  31. if(Dist[nx][ny] > Dist[x][y] + (i < 2 ? V : 1)) {
  32. Dist[nx][ny] = Dist[x][y] + (i < 2 ? V : 1);
  33. Q.push(make_pair(-Dist[nx][ny], make_pair(nx, ny)));
  34. }
  35. }
  36. }
  37. return INF;
  38. }
  39. inline bool Check(double V) {
  40. for(register int i = 1; i <= n; ++i)
  41. for(register int j = 1; j <= m; ++j) Vst[i][j] = 0, Dist[i][j] = INF;
  42. return Dijstra(V) <= X;
  43. }
  44. inline void Erfen() {
  45. double L = 0.0, R = 10.0;
  46. while(L + eps < R) {
  47. double mid = (L + R) / 2.0;
  48. if(Check(mid)) L = mid;
  49. else R = mid;
  50. }
  51. printf("%0.5lf\n", L);
  52. return ;
  53. }
  54. int main() {
  55. // freopen("maze.in", "r", stdin);
  56. // freopen("maze.out", "w", stdout);
  57. scanf("%d", &T);
  58. while(T--) {
  59. scanf("%lf%d%d", &X, &n, &m);
  60. memset(a, 0, sizeof(a));
  61. string c;
  62. getline(cin, c);
  63. for(register int i = 1; i <= n; ++i) {
  64. getline(cin, c);
  65. for(register int j = 1; j <= m; ++j) {
  66. if(c[j - 1] == 'S') sx = i, sy = j;
  67. if(c[j - 1] == 'E') ex = i, ey = j;
  68. if(c[j - 1] == '#') a[i][j] = 0;
  69. else a[i][j] = 1;
  70. }
  71. }
  72. Erfen();
  73. }
  74. return 0;
  75. }

T2 Puppet

Description

给你一个长为 的数组 ,和一个完全相同的数组

你需要从数组 中选出一个数 ,若 数组中存在元素 ,使得 ,则扔掉 ,否则只扔掉 ,得分加

输出得分最大可能是多少。

Solution

先手玩一下,考虑如果想要一段东西丢掉 个可以怎么构造方案。

将两个序列错开 位,即第 位与 位匹配。

那么只要保证:1.重合的位可以匹配,2.不重合的位不能匹配。

这样就可以判断一个序列是否可以丢掉 个。

所以单独算一个序列的答案只要枚举 然后 即可。

但是考虑整个问题时,整个序列可以被分为很多个段,每个段单独匹配。

所以就变成了一个经典的分段

用上述方式预处理 即可。

另:考场上没考虑到的一个正确性有关的问题,那就是段与段之间,错开的那部分有没有可能互相匹配。

实际上是有可能的,但有处理方法,只要把上下两排交换,也就是每次是交错的把他错开,那就不会有这样的问题,而实际上这个的答案是与不交换等价的。

Code

  1. //The Code Is From Dawn
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. inline int read() {
  5. int x = 0;
  6. char c = getchar();
  7. bool f = 0;
  8. while(!isdigit(c)) {
  9. if(c == EOF) return -1;
  10. f |= (c == '-'), c = getchar();
  11. }
  12. while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();
  13. return f ? -x : x;
  14. }
  15. const int maxn = 55;
  16. int n, a[maxn], f[maxn], g[maxn][maxn];
  17. int main() {
  18. // freopen("puppet.in", "r", stdin);
  19. // freopen("puppet.out", "w", stdout);
  20. while(~(n = read())) {
  21. for(register int i = 1; i <= n; ++i) a[i] = read();
  22. sort(a + 1, a + n + 1);
  23. memset(g, 0, sizeof(g));
  24. memset(f, 0, sizeof(f));
  25. for(register int i = 1; i <= n; ++i)
  26. for(register int j = i; j <= n; ++j) {
  27. for(register int k = 1; i + k - 1 <= j; ++k) {
  28. int flag = 1;
  29. for(register int p = 0; i + p + k <= j; ++p)
  30. if(a[i + k + p] - a[i + p] > 1) { flag = 0; break; }
  31. if(abs(a[j - k + 1] - a[i + k - 1]) <= 1) flag = 0;
  32. if(!flag) break;
  33. g[i][j] = k;
  34. }
  35. }
  36. for(register int i = 1; i <= n; ++i)
  37. for(register int j = 0; j < i; ++j) f[i] = max(f[i], f[j] + g[j + 1][i]);
  38. printf("%d\n", f[n]);
  39. }
  40. return 0;
  41. }

Summary

主要还是通过手玩发现单独把一个段匹配的算法,再去设计DP就显得比较简单。

T3 Chaser

Description

给你一条射线 和一条线段 ,点 从射线端点沿着射线以速度 运动,点 从点 开始,以速度 在线段上往返运动。

令点 的最短距离为 ,若 ,输出 Dangerous,若 ,则输出 Miss,否则输出 Perfect

Solution

考虑将其中一个点固定,例如可以将往返运动的点固定不动。

也就是更换参考系,重新计算另一个点的路线。

考试的时候傻掉了,实际上可以先把两个点的速度向量 分别求出

那么新的路线显然是折线,然后用向量运算把这个折线的速度向量算出来:,从而算出直线的向量表示。

然后可以发现这些周期性折线的两段会分别平行。

也就是可以将其划分为两组平行的周期性线段,然后求点到这两组线段的最小距离。

单看一组,点到这组线段的距离显然具有单峰性,考虑三分,然后套个点到线段的距离求法就行了。

另:点到线段的距离求法:

与两端点求叉积,得到平四面积,除掉线段的长度,得到点到该直线的距离。

用点积判断垂足是否落在线段上,若不在则与两端点求距离。

Code

  1. //The Code Is From Dawn
  2. #include<bits/stdc++.h>
  3. #define int long long
  4. using namespace std;
  5. inline int read() {
  6. int x = 0;
  7. char c = getchar();
  8. bool f = 0;
  9. while(!isdigit(c)) f |= (c == '-'), c = getchar();
  10. while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();
  11. return f ? -x : x;
  12. }
  13. const int INF = 1e18;
  14. struct node {
  15. double x, y;
  16. }W1, W2, T1, T2, Vw, Vt, P, Q, Cnt;
  17. double vw, vt, dl, dr, Answer, Time;
  18. inline node operator + (node A, node B) { return (node){A.x + B.x, A.y + B.y}; }
  19. inline node operator - (node A, node B) { return (node){A.x - B.x, A.y - B.y}; }
  20. inline node operator * (node B, double A) { return (node){A * B.x, A * B.y}; }
  21. inline node operator / (node B, double A) { return (node){B.x / A, B.y / A}; }
  22. inline double operator * (node A, node B) { return A.x * B.y - A.y * B.x; }
  23. inline double operator ^ (node A, node B) { return A.x * B.x + A.y * B.y; }
  24. inline double Len(node A) { return sqrt(A.x * A.x + A.y * A.y); }
  25. inline double Dis(node A, node B) { return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y)); }
  26. inline double Segment_Dist(node A, node L, node R) {
  27. // printf("%lf\n", Len(L -R));
  28. if(((A - L) ^ (R - L)) < 0.0 || ((A - R) ^ (L - R)) < 0.0) return min(Len(A - L), Len(A - R));
  29. else return fabs((A - L) * (A - R)) / Len(L - R);
  30. }
  31. inline void Work1() {
  32. int l = 0, r = 2e9;
  33. while(r - l > 12) {
  34. int mid1 = l + (r - l) / 3, mid2 = r - (r - l) / 3;
  35. if(Segment_Dist(T1, W1 + Cnt * mid1, P + Cnt * mid1) >= Segment_Dist(T1, W1 + Cnt * mid2, P + Cnt * mid2)) l = mid1;
  36. else r = mid2;
  37. }
  38. for(register int i = l; i <= r; ++i) Answer = min(Answer, Segment_Dist(T1, W1 + Cnt * i, P + Cnt * i));
  39. }
  40. inline void Work2() {
  41. int l = 0, r = 2e9;
  42. while(r - l > 12) {
  43. int mid1 = l + (r - l) / 3, mid2 = r - (r - l) / 3;
  44. if(Segment_Dist(T1, P + Cnt * mid1, Q + Cnt * mid1) >= Segment_Dist(T1, P + Cnt * mid2, Q + Cnt * mid2)) l = mid1;
  45. else r = mid2;
  46. }
  47. for(register int i = l; i <= r; ++i) Answer = min(Answer, Segment_Dist(T1, P + Cnt * i, Q + Cnt * i));
  48. }
  49. signed main() {
  50. // freopen("chaser.in", "r", stdin);
  51. // freopen("chaser.out", "w", stdout);
  52. while(~scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", &W1.x, &W1.y, &W2.x, &W2.y, &vw, &T1.x, &T1.y, &T2.x, &T2.y, &vt, &dl, &dr)) {
  53. Vw = (W2 - W1), Vw = Vw / Len(Vw) * vw, Vt = (T2 - T1), Vt = Vt / Len(Vt) * vt;
  54. Time = Len(T2 - T1) / vt, Answer = INF;
  55. P = W1 + (Vw - Vt) * Time, Q = P + (Vw + Vt) * Time, Cnt = Vw * (2.0 * Time);
  56. Work1(), Work2();
  57. if(Answer <= dl) printf("Dangerous\n");
  58. else if(Answer <= dr) printf("Perfect\n");
  59. else printf("Miss\n");
  60. }
  61. return 0;
  62. }

Summary

求两个动点间的动态距离,可以考虑更换参考系,使其只有一个动点,从而转化为定点与定线的问题。

转换参考系时,另一个点路径的改变就是做速度向量运算。

许多周期性折线,线段到点距离似乎都具有单峰或单调性,适合三分或二分。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注