首页 > 其他分享 >8 月 9 日测试题解

8 月 9 日测试题解

时间:2023-08-09 19:00:43浏览次数:30  
标签:std le gcd 测试题 int 复杂度 dis

集体被大样例薄纱了。

T1 P1292

有两个容量分别为 \(a\) 与 \(b\) 的酒杯与一个容量无限的酒桶,有以下几种操作:

  1. 用酒桶将 \(a\) 倒满;
  2. 将 \(b\) 中的酒全部倒入酒桶;
  3. 将 \(b\) 中的酒倒入 \(a\),直到 \(a\) 被装满或 \(b\) 被倒空。

问 \(a\) 中可能倒出的最少的酒是多少以及分别至少需要操作 \(1\) 与操作 \(2\) 几次。
\(a, b \le 10^9\) 且保证 \(a \le b\)。

被大样例骗了,以为是结论题,结果喜提 \(\text{30pts}\)。

容易发现当 \(a \perp b\) 时,最小的酒量显然为 \(1\)。那么推广到 \(a \not \perp b\) 上,由于我们知道 \(\frac{a}{\gcd(a, b)} \perp \frac{b}{\gcd(a, b)}\),且这是满足关系的最小的一对数,那么就有最小酒量为 \(\gcd(a, b)\)。

再考虑倒酒的次数,这等价于求线性方程 \(ax + by = \gcd(a, b)\) 的最小的一组非负解。首先使用 exgcd 求出满足关系的一组解 \(x', y'\),又该方程的通解形式为 \(\cases{x = x' + k\frac{b}{\gcd(a, b)} \\ y = y' - k\frac{a}{\gcd(a, b)}}\),那么就可以不断拓展求得最小的解,时间复杂度 \(\mathcal{O}(\log n)\)。

#include <bits/stdc++.h>
using i64 = long long;
int a, b;
int exgcd(int a, int b, int &x, int &y) {
  if (!b) return x = 1, y = 0, a;
  int d = exgcd(b, a % b, y, x);
  y -= a/ b * x;
  return d;
}
signed main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> a >> b;
  int x, y, g;
  std::cout << (g = exgcd(a, b, x, y)) << "\n";
  x *= -1, a *= -1;
  while (x < 0 || y < 0) {
    x += b / g * (x < 0);
    y -= a / g * (x >= 0);
  }
  std::cout << x << " " << y << "\n";
  return 0;
}

T2

一张点带权的图,定义删去一个点的代价为与其相连的所有点的点权和,求删去所有点的最小代价。
\(n \le 10^5\)。

不懂为什么要出这种仅靠猜答案就可以拿到满分的题。

观察样例可以发现最优方案显然为按点权从大到小删去,时间复杂度为 \(\mathcal{O}(n \log n)\)。

什么?你问我怎么得到的?猜的呗,OI 不需要证明!

如果你还不满足于上边的复杂度的话,容易发现对于任意一条边 \((u, v)\),其贡献为 \(\min(a_u, a_v)\),\(a\) 为点权。那么时间复杂度 \(\mathcal{O}(m)\)。

如果不会写代码的话建议退役或者发邮件给 Jeff Dean 让他帮你写。

T3

一张 \(n \times m\) 有障碍的网格图,现在给定起点 \(s = (sx, sy)\) 与 \(p\) 个点,要求经过这 \(p\) 个点的最短路以及 \(\max \sum_{(x, y) \in P} f(x, y)\),其中 \(f(x, y)\) 定义为网格图中以点 \((x, y)\) 为中心的最大无障碍正方形的边长的一半,\(P\) 为路径上的点集。
\(n, m \le 300\),\(p \le 15\)。

先考虑最短路。由于是不带权的网格图,其边权均为 \(1\),那么从一个点到另一个点的最短路容易用 BFS 在 \(\mathcal{O}(nm)\) 时间内求出。而经过全部 \(p\) 个点的最短路也是经典的状压 DP 问题 TSP 的常见变种之一,容易在 \(\mathcal{O}(p^2 2^p)\) 的时间内求解。

解决了最短路的问题之后再来看如何最大化 \(f\)。对于每一个点来说,\(f\) 都是独立的,因此我们可以先将其预处理出来。我的做法是考虑做四次递推,每次都求出对于每一个点以其作为正方形的四个端点之一的最大正方形边长,那么 \(f\) 便容易求出。这部分的时间复杂度为 \(\mathcal{O}(nm)\)。当然也有其他更为简便的做法,不过大概这种应该是比较好想的吧。

然后题目转化成了一个经典模型:有两种代价的最短(长)路。即在保证一种代价之和最优的情况下,尽量优化另一种代价。这是容易解决的,我们在 BFS 与 DP 的过程中多做一次判断即可。又由于这是一张无权的网格图,那么 BFS 过程不需要重新将某个点入队,即每一个点只需入队一次,因此复杂度仍然有保证。

总体时间复杂度为 \(\mathcal{O}(pnm + p^2 2^p)\)。

#include <bits/stdc++.h>
using i64 = long long;
using pii = std::pair<int, int>;
constexpr int N = 310, P = 16;
int n, m, t, a[N][N], sx, sy, p;
pii ed[P];
int enc[P];
int f[4][N][N], g[N * N];
std::vector<pii> adj[N * N];
int dis[P][N * N], val[P][N * N];
int dp1[1 << P][P], dp2[1 << P][P];
bool vis[N * N];
int encode(int i, int j) { return (i - 1) * m + j; }
pii decode(int i) { return std::make_pair(i / m + 1, i % m); }
void bfs(int s, int *dis, int *val, int siz = N * N) {
  std::queue<int> q;
  std::memset(dis, 0x3f, sizeof(int) * siz);
  std::memset(vis, 0, sizeof(vis));
  dis[s] = 0, val[s] = 0, q.push(s);
  vis[s] = true;
  while (!q.empty()) {
    int u = q.front();
    q.pop();
    for (auto [v, w] : adj[u]) {
      if (!vis[v]) {
        dis[v] = dis[u] + 1;
        val[v] = val[u] + g[v];
        q.push(v), vis[v] = true;
      } else if (dis[v] == dis[u] + 1 && val[v] < val[u] + g[v]) {
        val[v] = val[u] + g[v];
        q.push(v);
      }
    }
  }
}
signed main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n >> m >> t;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) std::cin >> a[i][j];
  }
  std::cin >> sx >> sy >> p;
  sx++, sy++;
  for (int i = 1; i <= p; i++) {
    auto &[l, r] = ed[i];
    std::cin >> l >> r, l++, r++;
  }
  int lx[4] = {1, 1, n, n}, ly[4] = {1, m, 1, m};
  int rx[4] = {n, n, 1, 1}, ry[4] = {m, 1, m, 1};
  int dx[4] = {1, 1, -1, -1}, dy[4] = {1, -1, 1, -1};
  for (int k = 0; k < 4; k++) {
    for (int i = lx[k]; i != rx[k] + dx[k]; i += dx[k]) {
      for (int j = ly[k]; j != ry[k] + dy[k]; j += dy[k]) {
        if (a[i][j]) continue;
        f[k][i][j] = std::min({f[k][i - dx[k]][j], f[k][i][j - dy[k]],
                               f[k][i - dx[k]][j - dy[k]]}) +
                     1;
      }
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (a[i][j]) continue;
      int cur = encode(i, j);
      g[cur] = std::min({f[0][i][j], f[1][i][j], f[2][i][j], f[3][i][j]}) - 1;
      g[cur] = std::min(t, g[cur]);
    }
  }
  dx[0] = 1, dx[1] = -1, dx[2] = 0, dx[3] = 0;
  dy[0] = 0, dy[1] = 0, dy[2] = 1, dy[3] = -1;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (a[i][j]) continue;
      for (int k = 0; k < 4; k++) {
        int cx = i + dx[k], cy = j + dy[k];
        if (a[cx][cy] || cx < 1 || cx > n || cy < 1 || cy > m) continue;
        adj[encode(i, j)].emplace_back(encode(cx, cy), 1);
      }
    }
  }
  ed[0] = std::make_pair(sx, sy);
  for (int i = 0; i <= p; i++) {
    enc[i] = encode(ed[i].first, ed[i].second);
    bfs(enc[i], dis[i], val[i]);
  }
  std::memset(dp1, 0x3f, sizeof(dp1));
  for (int i = 1; i <= p; i++) {
    dp1[1 << (i - 1)][i - 1] = dis[0][enc[i]];
    dp2[1 << (i - 1)][i - 1] = val[0][enc[i]];
  }
  for (int i = 0; i < 1 << p; i++) {
    for (int j = 0; j < p; j++) {
      if (!(i & (1 << j))) continue;
      for (int k = 0; k < p; k ++) {
        if (i & (1 << k)) continue;
        auto pre = dp1[i][j] + dis[j + 1][enc[k + 1]];
        if (dp1[i | (1 << k)][k] > pre) {
          dp1[i | (1 << k)][k] = pre;
          dp2[i | (1 << k)][k] = dp2[i][j] + val[j + 1][enc[k + 1]];
        } else if (dp1[i | (1 << k)][k] == pre &&
                   dp2[i | (1 << k)][k] < dp2[i][j] + val[j + 1][enc[k + 1]]) {
          dp2[i | (1 << k)][k] = dp2[i][j] + val[j + 1][enc[k + 1]];
        }
      }
    }
  }
  int ans1 = 0x3f3f3f3f, ans2 = 0;
  for (int i = 1; i <= p; i++) {
    if (dp1[(1 << p) - 1][i - 1] < ans1) {
      ans1 = dp1[(1 << p) - 1][i - 1];
      ans2 = dp2[(1 << p) - 1][i - 1];
    } else if (dp1[(1 << p) - 1][i - 1] == ans1) {
      ans2 = std::max(ans2, dp2[(1 << p) - 1][i - 1]);
    }
  }
  std::cout << ans1 << " " << ans2 + g[enc[0]] << "\n";
  return 0;
}

T4

给定一张 \(n\) 个点 \(m\) 条边的带权无向图,\(q\) 次询问,每次询问某个点到点 \(n\) 的最短路。
这里的路径长度定义稍有不同,设已经过的边集为 \(P\),\(g = \gcd_{i \in P} w_i\),当前边原始边权为 \(pre\),那么这条边的实际边权为 \(\frac{pre}{\gcd(pre, g)}\)。
\(n \le 1000\),\(n - 1 \le m \le 2000\),\(q \le 10^5\),最大边权 \(\max(w) \le 500\)。

赛时最后想了个很正确的分层图做法,是对的但是没有时间写了。

发现值域很小,那么我们考虑暴力分层图。将每个点拆成 \(500\) 个点,其中点 \((i, j)\) 表示走到 \(i\) 时边权 \(\gcd\) 为 \(j\) 的情况。容易发现 \((i, j)\) 只能向 \(\gcd\) 为 \(j\) 的因子的点转移,又由于题目要求的是到点 \(n\) 的最短路,那么可以考虑将图转置,即为求从点 \(n\) 出发的最短路,且 \((i, j)\) 只能向 \(j\) 的倍数转移。询问则直接暴力的枚举 \(\gcd\) 即可。

使用 Dijkstra 跑最短路,那么总体时间复杂度为 \(\mathcal{O}(n\max(w)\log n + q\max(w))\)。

#include <bits/stdc++.h>
using i64 = long long;
using pii = std::pair<int, int>;
using tiii = std::tuple<int, int, int>;
constexpr int N = 1050, M = 505;
int n, m, q;
std::vector<tiii> adj[N][M];
int dis[N][M];
bool vis[N][M];
void dijkstra(int s) {
  std::priority_queue<tiii, std::vector<tiii>, std::greater<tiii>> pq;
  std::memset(dis, 0x3f, sizeof(dis));
  std::memset(vis, 0, sizeof(vis));
  for (int i = 0; i <= 500; i++) pq.emplace(0, s, i), dis[s][i] = 0;
  while (!pq.empty()) {
    auto [_, u, i] = pq.top();
    pq.pop();
    if (vis[u][i]) continue;
    vis[u][i] = true;
    for (auto [v, k, w] : adj[u][i]) {
      if (dis[v][k] > dis[u][i] + w) {
        dis[v][k] = dis[u][i] + w, pq.emplace(dis[v][k], v, k);
      }
    }
  }
}
signed main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n >> m;
  for (int i = 1, u, v, w; i <= m; i++) {
    std::cin >> u >> v >> w;
    for (int j = 0; j <= 500; j++) {
      int g = std::__gcd(j, w);
      adj[v][g].emplace_back(u, j, w / g);
      adj[u][g].emplace_back(v, j, w / g);
    }
  }
  std::cin >> q;
  dijkstra(n);
  for (int i = 1, u; i <= q; i++) {
    std::cin >> u;
    int ans = 0x3f3f3f3f;
    for (int j = 1; j <= 500; j++) ans = std::min(ans, dis[u][j]);
    std::cout << ans << "\n";
  }
  return 0;
}

标签:std,le,gcd,测试题,int,复杂度,dis
From: https://www.cnblogs.com/forgot-dream/p/17617762.html

相关文章

  • 全选 和 不能全选 测试题 逻辑代码
    全选和不能全选测试题逻辑代码关于测试题会出现三种情况1.可以全选的点击就加入选中数组里面2.不可以全选的先点击可以多选的再点击不能多选的会选中数组情况3.不可以全选的先点击不能全选的再点击可以全选的不能全选的那个被取消可以全选的一个个添加......
  • 测试题分析
    目录T1计算表达式T2查找子串并替换T3AtoBT4回形取数T5算24点T1计算表达式题意表达式的形式如:3+5*6-4其中,运算数为一位整数,运算符为+、-、*三种,且运算符没有优先级的区分,一律自左向右计算。如上例的计算过程为:3+5*6-4=8*6-4=48-4=44分析模拟题,找到一种固......
  • 第二周周测试题
    第二周周测试题1.尽可能多的列举python字符串类型操作⽅法(⽅法名称+功能介绍).len()计算字符串的字符长度.strip()去除字符串首尾的特殊字符,默认是空格\n.replace()替换内容可以将字符串内的特定内容或字符进行替换.split()以特定条件切割字符串切割字符串后的每......
  • 6.3测试题以及参考答案(C++基础)
    测试题总分120,时间180分钟一、单选题(每题2分,共40分)C++中表示大于等于用以下哪个关系运算符(B)A.>B.>=C.≥D.>&=C++中,不等于用以下哪个关系运算符(C)A.<>B.≠C.!=D.==表达式7%2的值是多少(B)A.0B.1C.2D.-1要计算变量B的......
  • 6道Python简单的测试题,你知道答案吗?
    学Python光掌握基础理论知识是不够的,我们需要将理论知识转化为实战技能,本篇文章小编为大家整理了6道Python简单的测试题,快来检测一下你的Python基础怎么样!1、以下代码的输出结果为:print(round(-3.6))A.-4B.-4.0C.-3D.-3.02、以下代码的输出结果为......
  • 课堂测试题目
    2021级《软件工程》开发技能测试试卷(180分钟) 河北宏志大学学生成绩管理系统(卷面成绩40分) 河北宏志大学学生成绩管理系统1、项目需求:学生管理是各大院校的管理工作中尤为重视的一项工作,它一直以来是学校管理的一项重要的衡量指标。学生管理系统的应用解决了学校日常学生......
  • 测试题2 (对象锁-)
         ......
  • 模拟测试题解
    题目链接:https://vjudge.csgrandeur.cn/contest/557480AOJ维护中计算a+b(0<=a,b<=10)。点击查看代码#include<iostream>usingnamespacestd;intmain(){inta,b;cin>>a>>b;cout<<a+b;return0;}B不支持python队列报数,直接模拟即可。点击查看......
  • 4 月 30 日测试题解
    4月30日测试题解T1\({\color{green}{\text{100pts}}}\text{/100pts}\)题意一个无限长宽的棋盘,给出起点\(s\)和终点\(t\),行走方式是象棋中马的走法,问最少需要走多少步。对于\(100\%\)的数据,\(|x_s|,|y_s|,|x_t|,|y_t|\le10^7\)。思路\(\text{100pts}\)首先,坐......
  • 4 月 27 日测试题解
    4月27日测试题解最短路专场T1\({\color{green}{\text{100pts}}}\text{/100pts}\)题意给出\(m\)个变量与\(n\)个约束,每个约束形如以下三种中的一种;\(x_i-x_j\lew\)\(x_i-x_j\gew\)\(x_i-x_j=w\)有\(q\)个询问,每个询问为形如\((x_i,x_j)\)的二元......