首页 > 其他分享 >4 月 21 日测试题解

4 月 21 日测试题解

时间:2023-04-29 19:56:35浏览次数:36  
标签:std pre 21 测试题 int tree rc lc

4 月 21 日测试题解

T1 \({\color{green}{\text{100pts}}}\text{/100pts}\)

题意

给出平面上的两条线段,求线段之间的距离。

\(\text{|线段端点坐标|} \le 10^4\)。

思路

一开始想的是分讨,但是又怕自己写挂了,所以就写了三分套三分。至少这个不怕少讨论一个情况。

既然是三分套三分,那就没什么可说的了,其实就是三分的板子。

大致思路就是先在一条线段上三分出一个点,然后再在另外一条线段上三分出与它距离最小的点。

代码

#include <bits/stdc++.h>

using i64 = long long;
using f80 = long double;

namespace myFile {
void useFileIO() {
  freopen("segment.in", "r", stdin);
  freopen("segment.out", "w", stdout);
  return;
}
}

struct Point {
  f80 x, y;
  Point() {}
  Point(f80 x, f80 y) : x(x), y(y) {}
  
  Point operator= (const Point &rhs) {
    x = rhs.x, y = rhs.y;
    return *this;
  }
};

bool operator< (const Point &lhs, const Point &rhs) {
  if (lhs.x == rhs.x) {
    return lhs.y < rhs.y;
  } else {
    return lhs.x < rhs.x;
  }
}

int main() {
  myFile::useFileIO();
  
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  std::cout << std::fixed << std::setprecision(4);
  
  static const f80 EPS = 1e-8;
  
  std::vector<Point> points(4);
  for (int i = 0; i < 4; i++) {
    std::cin >> points[i].x >> points[i].y;
  }
  
  std::sort(points.begin(), points.begin() + 2);
  std::sort(points.begin() + 2, points.end());
  
  auto getMinDis = [&](const Point &u) -> f80 {
    Point l(points[2].x, points[2].y), r(points[3].x, points[3].y);
    if (l.x == r.x && l.y == r.y) {
      return std::hypot(l.x - u.x, l.y - u.y);
    }
    f80 res = LONG_LONG_MAX;
    int d;
    if (l.y <= r.y) {
      d = 1;
    } else {
      d = -1;
    }
    while (std::hypot(l.x - r.x, l.y - r.y) > EPS) {
      Point lmid(l.x + std::fabs(r.x - l.x) / 3, l.y + d * std::fabs(r.y - l.y) / 3);
      Point rmid(r.x - std::fabs(r.x - l.x) / 3, r.y - d * std::fabs(r.y - l.y) / 3);
      f80 d1 = std::hypot(lmid.x - u.x, lmid.y - u.y);
      f80 d2 = std::hypot(rmid.x - u.x, rmid.y - u.y);
      if (d1 > d2 + EPS) {
        l = lmid;
        res = std::min(res, d2);
      } else {
        r = rmid;
        res = std::min(res, d1);
      }
    }
    return res;
  };
  
  Point l(points[0].x, points[0].y), r(points[1].x, points[1].y);
  int d;
  if (l.y <= r.y) {
    d = 1;
  } else {
    d = -1;
  }
  f80 res = LONG_LONG_MAX;
  if (l.x == r.x && l.y == r.y) {
    std::cout << getMinDis(l) << "\n";
    return 0;
  }
  while (std::hypot(l.x - r.x, l.y - r.y) > EPS) {
    Point lmid(l.x + std::fabs(r.x - l.x) / 3, l.y + d * std::fabs(r.y - l.y) / 3);
    Point rmid(r.x - std::fabs(r.x - l.x) / 3, r.y - d * std::fabs(r.y - l.y) / 3);
    f80 d1 = getMinDis(lmid), d2 = getMinDis(rmid);
    if (d1 > d2 + EPS) {
      l = lmid;
      res = std::min(res, d2);
    } else {
      r = rmid;
      res = std::min(res, d1);
    }
  }
  
  std::cout << res << "\n";
  
  return 0;
}

T2 \({\color{orange}{\text{60pts}}}\text{/100pts}\)

题意

给出一棵二叉树,树上的非叶子节点都有其对应的一个逻辑运算(与、或、非三种之一),节点的值为其两个子节点做对应逻辑运算后得到的值,给出所有叶子结点的初始值,问至少要修改几个叶子节点的值,才能改变树根结点的值。

\(1 \le n \le 2^{17} - 1\)。

思路

这道题基本上就是树形 DP 的裸题,题目中的信息明显在暗示根节点的答案是由子树的答案合并得到的。

于是我们就可以写出方程,记当前节点指定的逻辑算符为 \(\delta\),\(f_i\) 为改变当前节点的最小改动个数,\(pre_i\) 为节点 \(i\) 开始的值,则有:

\[f_i = \begin{cases} 1 & i \text{ is a leaf;} \\ \min(f_{lc_i}, f_{rc_i}) & \begin{cases} \delta = \wedge \text{ and } pre_i = 1; \\ \delta = \vee \text{ and } pre_i = 0; \\ \delta = \oplus; \\ \end{cases} \\ [pre_{lc_i} = 0] \times f_{lc_i} + [pre_{rc_i} = 0] \times f_{rc_i} & \delta = \wedge \text{ and } pre_i = 0; \\ [pre_{lc_i} = 1] \times f_{lc_i} + [pre_{rc_i} = 1] \times f_{rc_i} & \delta = \vee \text{ and } pre_i = 1. \\ \end{cases} \]

有了方程,代码就很容易写了,具体就是两遍 DFS,一遍求出 \(pre\),另一遍做 DP。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;

namespace myFile {
void useFileIO() {
  freopen("logical.in", "r", stdin);
  freopen("logical.out", "w", stdout);
  return;
}
}

struct Tree {
  int n;
  std::vector<std::vector<int>> ch;
  std::vector<int> type, f, pre;
  
  Tree(int n) : n(n + 1) {
    ch.resize(n + 1);
    type.resize(n + 1, 0);
    f.resize(n + 1, 0);
    pre.resize(n + 1, 0);
    return;
  }
  
  bool add(int u, int lc, int rc) {
    ch[u].resize(2);
    ch[u][0] = lc, ch[u][1] = rc;
    return true;
  }
  
  void dfs(int u) {
    if (ch[u].empty()) {
      return;
    }
    int lc = ch[u][0], rc = ch[u][1];
    dfs(lc), dfs(rc);
    if (type[u] == 1) {
      pre[u] = pre[lc] & pre[rc];
    } else if (type[u] == 2) {
      pre[u] = pre[lc] | pre[rc];
    } else {
      pre[u] = pre[lc] ^ pre[rc];
    }
    return;
  }
  
  void solve(int u) {
    if (ch[u].empty()) {
      f[u] = 1;
      return;
    } 
    int lc = ch[u][0], rc = ch[u][1];
    solve(lc), solve(rc);
    if (type[u] == 1) {
      if (pre[u]) {
        f[u] = std::min(f[lc], f[rc]);
      } else {
        f[u] = (!pre[lc]) * f[lc] + (!pre[rc]) * f[rc];
      }
    } else if (type[u] == 2) {
      if (pre[u]) {
        f[u] = pre[lc] * f[lc] + pre[rc] * f[rc];
      } else {
        f[u] = std::min(f[lc], f[rc]);
      }
    } else {
      f[u] = std::min(f[lc], f[rc]);
    }
    return;
  }
};

int main() {
  // myFile::useFileIO();
  
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  
  int n, m;
  std::cin >> n >> m;
  
  Tree tree(n);
  int root = 1;
  for (int i = 0; i < n; i++) {
    int x, y, z, a;
    std::cin >> x >> y >> z >> a;
    if (!z) {
      root = i + 1;
    }
    x == 0 || tree.add(i + 1, x, y);
    tree.type[i + 1] = a;
  }
  for (int i = 0; i < m; i++) {
    int x, y;
    std::cin >> x >> y;
    tree.pre[x] = y;
  }
  
  tree.dfs(root), tree.solve(root);
  std::cout << tree.f[root] << "\n";
  
  return 0;
}

T3 \({\color{red}{\text{10pts}}}\text{/100pts}\)

题意

给出 \(n\) 个元素的字符串序列 \(s\),每个字符串有一个偏爱值 \(a_i\),现在要求构造一个长度为 \(l\) 的字符串,记一种方法所使用的字符串下标集合为 \(S\),则这种构造方案的喜爱值为:

\[\sum_{s \in S} a_s \]

求喜爱值的最大值。

\(n \le 1000\),\(|s_i| \le 50\),\(l \le 100\)。

思路

\(\text{30pts}\)

这部分数据保证 \(n = 1\)。

考虑递推,由于只有一个串,我们容易想到KMP 构造 \(nxt\) 数组,由于 \(nxt\) 是最长公共前后缀,所以我们可以用 \(nxt\) 来状态转移。时间复杂度为 \(O(\alpha l)\),其中 \(\alpha\) 是常数,表示 \(nxt\) 能跳的最大次数。

\(\text{100pts}\)

仍然使用递推,考虑对 \(s\) 中的串建出 AC 自动机,记自动机中的转移数组为 \(nxt\),定义状态为 \(f_{i, j}\),表示构造的字符串的第 \(i\) 位使用字典树中标号为 \(j\) 的字符所能得到的最大喜爱值,则有:

\[f_{i + 1, j} = \max_{k \in fa} f_{i, k} + val \]

其中 \(fa\) 为能够转移到 \(j\) 的集合,\(val\) 为转移过程中累加的喜爱值。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;
using i64u = unsigned long long;
using f80 = long double;

namespace MyFile {
void useFileInuput() {
  #ifndef ONLINE_JUDGE
  freopen("tmp.in", "r", stdin);
  freopen("tmp.out", "w", stdout);
  #endif
  return;
}
}

struct ACAM {
  std::vector<std::vector<int>> tree;
  std::vector<int> end, fail;

  ACAM() {
    tree.emplace_back(26, 0);
    end.push_back(0), fail.push_back(0);
    return;
  }

  void insert(const std::string &s, const int &val) {
    int pivot = 0;

    for (int i = 0; i < s.length(); i++) {
      int d = s[i] - 'a';
      if (!tree[pivot][d]) {
        tree[pivot][d] = tree.size();
        tree.emplace_back(26, 0);
        end.push_back(0), fail.push_back(0);
      }
      pivot = tree[pivot][d];
    }
    end[pivot] += val;

    return;
  }

  void build() {
    static std::queue<int> q;
    for (int i = 0; i < 26; i++) {
      if (tree[0][i]) {
        q.push(tree[0][i]);
      }
    }
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      for (int i = 0; i < 26; i++) {
        int &v = tree[u][i];
        if (v) {
          if (u) {
            fail[v] = tree[fail[u]][i];
          }
          q.push(v);
        } else {
          v = tree[fail[u]][i];
        }
      }
    }
    return;
  }

  void solve(const int &l) {
    std::vector f(2, std::vector<int>(tree.size(), INT_MIN));
    f[0][0] = 0;
    int cur = 0;
    for (int i = 0; i < l; i++) {
      for (int j = 0; j < 26; j++) {
        for (int k = 0; k < tree.size(); k++) {
          int tmp = 0;
          for (int pivot = k; pivot; pivot = fail[pivot]) {
            tmp += end[pivot];
          }
          f[cur ^ 1][tree[k][j]] = std::max(f[cur ^ 1][tree[k][j]], f[cur][k] + tmp);
        }
      }
      for (int i = 0; i < tree.size(); i++) {
        f[cur][i] = INT_MIN;
      }
      cur ^= 1;
    }

    int ans = INT_MIN;
    for (int i = 0; i < tree.size(); i++) {
      ans = std::max(ans, f[cur][i]);
    }

    std::cout << ans << "\n";

    return;
  }

};

signed main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);

  int n, l;
  std::cin >> n >> l;

  std::vector<int> a(n);
  for (int i = 0; i < n; i++) {
    std::cin >> a[i];
  }

  ACAM acam;
  for (int i = 0; i < n; i++) {
    std::string s;
    std::cin >> s;
    acam.insert(s, a[i]);
  }

  acam.build();
  acam.solve(l + 1);

  return 0;
}

标签:std,pre,21,测试题,int,tree,rc,lc
From: https://www.cnblogs.com/forgot-dream/p/17364411.html

相关文章

  • [CEOI2021] Newspapers
    模拟赛没有判\(n=1\),喜提\(0\)分。感谢每个subtask都放\(n=1\)的善良出题人。看到题感觉A的操作好像比较弱小,唯一的用处似乎只能用来排除B在哪些位置,那这样就有一个暴力了,直接记录当前还有哪些点上可能有B,然后直接跑bfs,就可以通过第一档分了。看到第二档分似乎比较......
  • P7603 [THUPC2021] 鬼街(减半警报器模板)
    P7603[THUPC2021]鬼街(减半警报器模板)前言这是一个由lxl大佬提出的神奇trick,第一次省选集训的时候有点颓,听完了没写。刚好明天又要讲这个不如写篇题解。还是,我太弱了;所以又是研究一晚上才写出来,所以还是吧我对这道题的理解讲讲。正文何为折半报警器按照lxl的ppt上的......
  • 4.21
    学习时间:2h代码量:200我今天上了软件工程课,小组作业写的不太合格,我们争取这个假期结束之前完成更改我开始了补习html遗漏的知识,学习了很多的标签,和很多属性<%@pageimport="DAO.dao"%><%@pageimport="java.sql.*"language="java"contentType="text/html;charset=utf-8"......
  • 20042124_chappie
    [换成自己的源]docker-machinesshdefaultsed-i"s|EXTRA_ARGS='|EXTRA_ARGS='--registry-mirror=https://2w188x2k.mirror.aliyuncs.com|g"/var/lib/boot2docker/profileexitdocker-machinerestartdefault [打开rknndocker]dockerrun-t-i--privilege......
  • 21章
    第21章存储秘密怎样存储像口令和密钥这样的长期秘密呢?这个秘密应该保持其私密性丢失这个秘密的风险应该尽可能小21.1磁盘直接办法:把秘密存储在计算机的硬盘上或其他永久存储介质上问题:需确保设备安全、用户可能会用多台设备、秘密不便携更好的解决方案:让Alice把密钥存储......
  • IDEA从零到精通(21)之使用Maven clean发生错误Process terminated
    IDEA从零到精通(21)之使用Mavenclean发生错误Processterminated原文链接:https://blog.csdn.net/dkm123456/article/details/121871870文章目录作者简介引言导航热门专栏推荐错误描述解决方案:再次clean小结导航热门专栏推荐作者简介作者名:编程界明世隐简介:CSDN博客......
  • 【做题笔记】洛谷 P7987 [USACO21DEC] Paired Up G
    在我的个人博客获得更好的阅读体验Problem洛谷P7987[USACO21DEC]PairedUpG题目大意:有\(n\)个点,其中第\(i\)个点位置为\(x_i\),权值为\(y_i\)。若两个点\(i,j\)满足\(|x_i-x_j|\lek\),则这两个点之间有一条边。求一个匹配,在满足其为极大匹配的情况下匹配点的......
  • questions_02:【KeyError: 'mobile_phone'[27/Apr/2023 21:42:21] "POST /register/ H
    BUG在成功注册之后,如果填写相同的信息,会报出一个【KeyError:'mobile_phone'[27/Apr/202321:42:21]"POST/register/HTTP/1.1"50086526】的bug,原因是我们的cleaned_data中的数据是按照fields中的顺序去校验成功之后添加的,所以当出现相同的数据时候cleaned_data前面几个字......
  • 4.21今日总结
    内置信号和自定义槽使用实例实现过程同上述步骤一样。槽函数showMsg为自定义函数。信号与槽:self.pushButton.clicked.connect(self.showMsg)完整代码如下(可直接拷贝运行,字体加粗部分为添加部分):#-*-coding:utf-8-*-#Formimplementationgeneratedfromreadinguifile......
  • 基于ISO 21434的汽车网络安全实践
    “转载自维克多汽车技术(上海)有限公司,作者VectorChina”商业领域的IT系统和嵌入式产品的IT系统正在融合为一种多功能系统。相应地,关注汽车网络安全的ISO21434标准应运而生。该标准的意义在于提供了一个指南,可用于降低产品、项目和组织中存在的安全风险。为了有效实施ISO21434标......