首页 > 其他分享 >JOI23-24 Final

JOI23-24 Final

时间:2024-09-05 23:47:18浏览次数:2  
标签:24 JOI23 le dist int MAXN const Final dp

A. Room Temperature

题目描述

有 \(N\) 个人,每个人都有一个适宜温度 \(A_i\)。现在有无限件外套,一个人每穿一件外套适宜温度就会减 \(T\)。

如果当前室温为 \(x\),则一个人不适程度为 \(|x-A_i|\)。求所有人的不适程度最大值的最小值。

思路

首先我们可以令 \(A_i \leftarrow A_i \bmod T\)。(可以通过穿很多件外套得到)此时不适程度为 \(\lceil\frac{\max-\min}{2}\rceil\)。但这不一定是最优解,我们还可以通过脱外套来减少不适程度。所以可以用一种类似于断环成链的方法来求解

空间复杂度 \(O(N)\),时间复杂度 \(O(N\log N)\)。

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1000001;

int n, t, a[MAXN], ans;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> t;
  ans = t;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
    a[i] %= t;
  }
  sort(a + 1, a + n + 1);
  for(int i = 1; i <= n; ++i) {
    a[i + n] = a[i] + t;
    ans = min(0ll + ans, (0ll + a[i + n - 1] - a[i] + 1) / 2);
  }
  cout << ans;
  return 0;
}
/*
6 8
24 22 21 25 29 17
*/

B. Construction Project 2

题目描述

给定一个 \(N\) 个点,\(M\) 条边的无向带权图,你可以选择两个数 \(u,v\) 使得 \(1\le u < v \le N\),并在 \(u\) 和 \(v\) 之间建一条边权为 \(L\) 的边。问有多少种建边的方法使得 \(S\) 到 \(T\) 的最短路 \(\le K\)。

思路

首先从 \(S\) 和 \(T\) 跑一遍最短路。如果此时的最短路就已经 \(\le K\) 了,那么直接输出 \(\frac{N(N-1)}{2}\)。

否则,很明显要满足 \(dist_{S,u}+L+dist_{v,T}\le K\),所以就对从 \(S,T\) 出发的最短路排个序,然后做双指针就行了。

但是为什么这样不会把 \((u,v)\) 和 \((v,u)\) 都算一遍呢?可以发现这里我们的判断是把建的这条边看做一条有向边的(因为我们只判断一个方向)。

假设两个方向都是满足条件的,即

\[\begin{array}{l} dist_{S,u}+L+dist_{v,T}\le K\\ dist_{S,v}+L+dist_{u,T}\le K \end{array} \]

此时一定修改了另一端的最短路,否则这条路径不可能 \(\le K\),即

\[dist_{S,u}+L<dist_{S,v}\\ dist_{S,v}+L<dist_{S,u} \]

又因为 \(dist_{S,u}+L<dist_{S,v}\),所以 \(dist_{S,u}+L+L+dist_{u,T}\le K\)。而这里 \(dist_{S,u}+dist_{u,T}\) 就是一条从 \(S\) 到 \(T\) 的 \(\le K\) 的路径了,而这种情况我们在一开始就已经处理过了,所以不会有重复。

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;

const int MAXN = 200001;
const ll INF = (ll)(1e18);

struct Node {
  int u;
  ll dis;
};

struct cmp {
  bool operator()(const Node &a, const Node &b) const {
    return a.dis > b.dis;
  }
};

int n, m, s, t, l;
ll dist[2][MAXN], ans, k;
bool vis[2][MAXN];
vector<pii> e[MAXN];

void dij(int S, bool op) {
  priority_queue<Node, vector<Node>, cmp> pq;
  for(int i = 1; i <= n; ++i) {
    dist[op][i] = INF;
    vis[op][i] = 0;
  }
  dist[op][S] = 0;
  pq.push({S, 0});
  for(; !pq.empty(); ) {
    auto [u, dis] = pq.top();
    pq.pop();
    if(vis[op][u]) {
      continue;
    }
    vis[op][u] = 1;
    for(auto [v, w] : e[u]) {
      if(dis + w < dist[op][v]) {
        dist[op][v] = dis + w;
        pq.push({v, dis + w});
      }
    }
  }
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> m >> s >> t >> l >> k;
  for(int i = 1, u, v, w; i <= m; ++i) {
    cin >> u >> v >> w;
    e[u].emplace_back(v, w);
    e[v].emplace_back(u, w);
  }
  dij(s, 0);
  dij(t, 1);
  if(dist[0][t] <= k) {
    cout << 1ll * n * (n - 1) / 2;
    return 0;
  }
  sort(dist[0] + 1, dist[0] + n + 1);
  sort(dist[1] + 1, dist[1] + n + 1);
  for(int i = 1, j = n; i <= n; ++i) {
    for(; j > 0 && dist[0][i] + dist[1][j] + l > k; --j) {
    }
    ans += j;
  }
  cout << ans;
  return 0;
}

C. Marathon Race 2

题目描述

在一根数轴上有 \(N\) 个球,第 \(i\) 个球位于位置 \(X_i\)。你可以做以下操作:

  • 在数轴上移动一个单位,花费 \(x+1\) 的时间。这里 \(x\) 是你手上球的数量
  • 拿起一个球,花费 \(1\) 的时间。

有 \(Q\) 次询问,每次一开始你在位置 \(S\),你要带着所有球去到位置 \(G\) ,问你能不能在 \(T\) 的时间内完成?

思路

由于拿球很难解决,所以我们把它倒过来,变成放球。而放球很明显每遇到一个放球的位置就一定会放下。

因此放完球的区域一定是一个区间。所以考虑区间 dp。

首先将 \(X\) 排序。

令 \(dp_{0/1,l,r}\) 表示现在已经把 \(X_l\) 到 \(X_r\) 放好,当前在 \(l/r\),把剩下的球放好所需的最少时间。

很明显我们有:

\[dp_{0,l,r} = \min(dp_{0,l-1,r}+(X_l-X_{l-1})\cdot (n-r+l),dp_{1,l,r+1}+(X_{r+1}-X_l)\cdot(n-r+l))\\ dp_{1,l,r} = \min(dp_{0,l-1,r}+(X_r-X_{l-1})\cdot (n-r+l),dp_{1,l,r+1}+(X_{r+1}-X_r)\cdot(n-r+l)) \]

但是我们还没有解决起点和终点的问题:

  • 对于终点,我们可以枚举选择离 \(G\) 最近的两个点作为终点,并求出走到 \(G\) 的时间。

  • 对于起点,我们可以在状态中加入一位 \(0/1\),表示起点在 \(X_1/X_N\)。对于 \(dp_0/1\) 的 dp,其初始状态为 \(dp_{x,x,1,N}(x\in\{0,1\})\)。

但是此时时间复杂度不正确,要如何优化呢?

观察到题目的 \(T\) 只有 \(5\times 10^5\),我们可以想到,如果不同 \(X_i\) 的数量为 \(x\) ,则至少需要 \(2+3+\dots+x+N\) 的时间,也就是只有 \(x\le O(\sqrt T)\approx 1000\) 时才有可能成功,因此我们可以把多个 \(X_i\) 相同的球压成一个做 dp,如果最终数量 \(>1005\) 就全部输出 NO

空间复杂度 \(O(T+N)\),时间复杂度 \(O(N\log N+T+Q)\)。

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXL = 500001, MAXN = 1010;
const ll INF = (ll)(1e18);

struct Node {
  int x, cnt;
}s[MAXN];

int n, l, q, tot, mp[MAXL], sum[MAXN];
ll dp[2][2][MAXN][MAXN];

int Calc(int l, int r) {
  return sum[r] - sum[l - 1];
}

int Binary_Search(int x) {
  int l = 1, r = tot + 1;
  for(; l < r; ) {
    int mid = (l + r) >> 1;
    (s[mid].x >= x ? r = mid : l = mid + 1);
  }
  return l;
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> l;
  for(int i = 1, x; i <= n; ++i) {
    cin >> x;
    mp[x]++;
  }
  cin >> q;
  for(int i = 0; i <= l; ++i) {
    if(mp[i]) {
      s[++tot] = {i, mp[i]};
      sum[tot] = sum[tot - 1] + mp[i];
      if(tot == 1005) {
        for(int j = 1; j <= q; ++j) {
          cout << "No\n";
        }
        return 0;
      }
    }
  }
  for(bool op : {0, 1}) {
    for(bool opt : {0, 1}) {
      for(int l = 0; l <= tot + 1; ++l) {
        for(int r = 0; r <= tot + 1; ++r) {
          dp[op][opt][l][r] = INF;
        }
      }
    }
    dp[op][op][1][tot] = 0;
    for(int len = tot - 1; len >= 1; --len) {
      for(int l = 1, r = len; r <= tot; ++l, ++r) {
        dp[op][0][l][r] = min(dp[op][0][l - 1][r] + 1ll * (n - Calc(l, r) + 1) * (s[l].x - s[l - 1].x),
                              dp[op][1][l][r + 1] + 1ll * (n - Calc(l, r) + 1) * (s[r + 1].x - s[l].x));
        dp[op][1][l][r] = min(dp[op][1][l][r + 1] + 1ll * (n - Calc(l, r) + 1) * (s[r + 1].x - s[r].x),
                              dp[op][0][l - 1][r] + 1ll * (n - Calc(l, r) + 1) * (s[r].x - s[l - 1].x));
      }
    }
  }
  for(int i = 1, S, T, ti; i <= q; ++i) {
    cin >> S >> T >> ti;
    int x = Binary_Search(T);
    ll ans = INF;
    if(x <= tot) {
      ans = min({ans, dp[0][0][x][x] + 1ll * (s[x].x - T) * (n + 1) + abs(S - s[1].x) + n,
                      dp[1][0][x][x] + 1ll * (s[x].x - T) * (n + 1) + abs(S - s[tot].x) + n});
    }
    if(x > 1) {
      ans = min({ans, dp[0][0][x - 1][x - 1] + 1ll * (T - s[x - 1].x) * (n + 1) + abs(S - s[1].x) + n,
                      dp[1][0][x - 1][x - 1] + 1ll * (T - s[x - 1].x) * (n + 1) + abs(S - s[tot].x) + n});
    }
    cout << (ans <= ti ? "Yes\n" : "No\n");
  }
  return 0;
}

D. Gift Exchange

题目描述

有 \(N\) 个小朋友,第 \(i\) 个小朋友有一个价值为 \(A_i\) 的礼物,希望得到价值为 \(B_i(B_i<A_i)\) 的礼物。有 \(Q\) 次询问,每次询问一个区间 \([l,r]\) 是否存在一种交换礼物的方案。

这里一种交换礼物的方案是指一个排列 \(p_1,p_2,\dots,p_N\),其中 \(p_i\ne i\) 且 \(A_{p_i}>B_i\)。

思路

首先,如果每个人都存在另一个人可以与其互换礼物,那么这就是一个可行的区间。

假设我们已经按照 \(A_i\) 排序了,那么此时对于 \(i<j\),\(i\) 都能接受 \(j\) 的礼物。此时只要 \(j\) 能接受 \(i\) 的礼物,那么 \([i,j]\) 这个区间都能构成一个环,而既然 \(j\) 能接受 \(i\) 的礼物,那么一定也能接受 \(i+1,i+2,\dots,j-1\) 的礼物,也就是所有人都能跟 \(j\) 互换礼物,满足条件。

但这是 \(A_i\) 已经排序好的情况下,实际我们并不能这么做。

所以我们先想出一个大致的思路再一点一点实现:

  • 对于每个 \(i\),求出在左边离他最近的可以互换礼物的人 \(l_i\) 和右边的 \(r_i\)。

  • 只要 \(\forall i\in [L,R]\) 都满足 \(l_i\ge L 或 r_i \le R\),则该区间合法。

首先考虑第一个步骤:如何求出 \(l_i,r_i\):

我们可以开一个 \(2N\) 大小的线段树,其中每个位置都存储着 \((A_i,i)\)。我们依次枚举每个 \(i\),找到线段树中 \([1,A_i-1]\) 这段区间,找出里面所有 \(A_j > B_i\) 的 \(j\) 并让 \(r_j \leftarrow i\),并从线段树中删掉 \(j\)。最后插入 \((A_i,i)\)。

我们在线段树中维护 \(A_i\) 的最大值,如果一个区间的最大值已经 \(\le B_i\),那么停止递归。

这样每个 \(i\) 只会被发现并删除一次,总时间复杂度 \(O(N\log N)\)。

求 \(l_i\) 只需变成倒着枚举即可。

对于查询,我们可以离线做。首先把查询按 \(R\) 排序。

接着使用一个线段树,维护 \(l_i\) 的最小值。我们使用双指针的方法,依次枚举 \(i\le R\),如果 \(i=r_j\),那么位置 \(j\) 已经合法,无需管它的 \(l_j\),所以把 \(lj\) 设为 \(\infty\)。

找到 \(i=r_j\) 的 \(j\) 可以用 vector 存储,而判断合法就是 \(\min \limits_{i=L}^{R} \{l_i\}\ge L\)。

空间复杂度 \(O(N)\),时间复杂度 \(O(N\log N+Q\log Q)\)。

代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1000001;

int L[MAXN], R[MAXN];

struct Max_Segment_Tree {
  int l[MAXN << 2], r[MAXN << 2], Max[MAXN << 2], id[MAXN << 2];
  void build(int u, int s, int t) {
    l[u] = s, r[u] = t, Max[u] = id[u] = 0;
    if(s == t) {
      return;
    }
    int mid = (s + t) >> 1;
    build(u << 1, s, mid), build((u << 1) | 1, mid + 1, t);
  }
  void update(int u, int p, int x, int _id) {
    if(l[u] == r[u]) {
      Max[u] = x, id[u] = _id;
      return;
    }
    (p <= r[u << 1] ? update(u << 1, p, x, _id) : update((u << 1) | 1, p, x, _id));
    Max[u] = max(Max[u << 1], Max[(u << 1) | 1]);
  }
  void GetLR(int u, int s, int t, int x, int _id, bool op) {
    if(r[u] < s || l[u] > t || Max[u] <= x) {
      return;
    }
    if(l[u] == r[u]) {
      (op ? R[id[u]] = _id : L[id[u]] = _id);
      Max[u] = id[u] = 0;
      return;
    }
    GetLR(u << 1, s, t, x, _id, op), GetLR((u << 1) | 1, s, t, x, _id, op);
    Max[u] = max(Max[u << 1], Max[(u << 1) | 1]);
  }
}tr;

struct Min_Segment_Tree {
  int l[MAXN << 2], r[MAXN << 2], Min[MAXN << 2];
  void build(int u, int s, int t) {
    l[u] = s, r[u] = t, Min[u] = int(1e9);
    if(s == t) {
      return;
    }
    int mid = (s + t) >> 1;
    build(u << 1, s, mid), build((u << 1) | 1, mid + 1, t);
  }
  void update(int u, int p, int x) {
    if(l[u] == r[u]) {
      Min[u] = x;
      return;
    }
    (p <= r[u << 1] ? update(u << 1, p, x) : update((u << 1) | 1, p, x));
    Min[u] = min(Min[u << 1], Min[(u << 1) | 1]);
  }
  int Getmin(int u, int s, int t) {
    if(l[u] >= s && r[u] <= t) {
      return Min[u];
    }
    int x = int(1e9);
    if(s <= r[u << 1]) {
      x = min(x, Getmin(u << 1, s, t));
    }
    if(t >= l[(u << 1) | 1]) {
      x = min(x, Getmin((u << 1) | 1, s, t));
    }
    return x;
  }
}TR;

struct Node {
  int l, r, id;
}s[MAXN];

int n, q, a[MAXN], b[MAXN];
bool ans[MAXN];
vector<int> ve[MAXN];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  for(int i = 1; i <= n; ++i) {
    cin >> b[i];
  }
  tr.build(1, 1, 2 * n);
  for(int i = 1; i <= n; ++i) {
    tr.GetLR(1, 1, a[i], b[i], i, 1);
    tr.update(1, b[i], a[i], i);
  }
  tr.build(1, 1, 2 * n);
  for(int i = n; i >= 1; --i) {
    tr.GetLR(1, 1, a[i], b[i], i, 0);
    tr.update(1, b[i], a[i], i);
  }
  cin >> q;
  for(int i = 1; i <= q; ++i) {
    cin >> s[i].l >> s[i].r;
    s[i].id = i;
  }
  sort(s + 1, s + q + 1, [=](const Node &a, const Node &b) -> bool {
    return a.r < b.r;
  });
  for(int i = 1; i <= n; ++i) {
    if(R[i]) {
      ve[R[i]].emplace_back(i);
    }
  }
  TR.build(1, 1, n);
  for(int i = 1, j = 1; i <= q; ++i) {
    for(; j <= n && j <= s[i].r; ++j) {
      TR.update(1, j, L[j]);
      for(int x : ve[j]) {
        TR.update(1, x, int(1e9));
      }
    }
    ans[s[i].id] = (TR.Getmin(1, s[i].l, s[i].r) >= s[i].l);
  }
  for(int i = 1; i <= q; ++i) {
    cout << (ans[i] ? "Yes\n" : "No\n");
  }
  return 0;
}

标签:24,JOI23,le,dist,int,MAXN,const,Final,dp
From: https://www.cnblogs.com/yaosicheng124/p/18398981

相关文章

  • 2024.9.4 leetcode 169 多数元素 (哈希表)
    题面 169.多数元素-力扣(LeetCode)题解:复习(自学)了一下哈希表,unordered_map<int,int>umap定义一个表umap.find(nums[i])!=umap.end()判断是否存在umap.insert({nums[i],1})插入umap.erase(nums[i])清除C++容器类<unordered_map>|菜鸟教程(runoob.com)class......
  • 24数学建模ABCE题 保姆级建模思路+可执行代码
    获取资料看文章末尾呢!!!!24数学建模国赛A题详细建模过程+可视化图表+参考论文文本中的第一个问题是关于舞龙队沿等距螺线盘入时的位置和速度计算。具体要求是:1.舞龙队沿螺距为55cm的等距螺线顺时针盘入,龙头前把手的行进速度始终保持1m/s。2.初始时,龙头位于螺线第16......
  • MySQL 源码|67 - 语法解析(V2):数值字面值|V20240905
    目录文档:MySQL源码|源码剖析文档目录源码位置(版本=MySQL8.0.37):/sql/sql_yacc.yy前置文档:MySQL源码|33-语法解析:bison基础语法规则根据MySQL源码|21-词法解析:状态转移逻辑梳理中梳理的MySQL词法解析逻辑,有如下终结符与数值相关:终结符名称终结符表示内容NUM......
  • 24年9月最新微软copilot国内Windows11强制开启使用教程方法
    几个月前就听说了微软的copilot加PC。今天新电脑到货。把系统都更新完以后。就研究了一下怎么开通这个copilot。正常情况下,目前国内的电脑是没有这个功能的。但只是因为地区不支持我们只需要通过一些简单的设置就可以让它显示出来嗯,最好是要Windows11的电脑系统其他的没有测试......
  • 2024.9.5 leetcode 3174 清除数字(字符串)
    题面3174.清除数字-力扣(LeetCode)题解:今天的每日一题比较简单,思路是遍历字符串,遇到第一个数字x的时候,把数字x和前面的字母y删除,也就是删除yx。1、为什么前面一定是字母,因为遇到的是第一个数字,前面不可能再有数字。2、如何实现删除yx,重新定义一个字符串,每一次遍历将y前面的字......
  • 【详细解析】2024高教社杯全国大学生数学建模竞赛B题思路
    ......
  • 三、搭建网站服务器超详细步骤——FinalShell下载安装使用流程(免费国产的SSH工具)+宝塔
    前言本篇博客是搭建网站服务器模块下的第3部分  FinalShell下载安装使用流程  在分享这篇博客之前,首先讲一下,FinalShell软件是干什么用的,用大白话进行说明一下:这个软件是一款远程控制和管理服务器的软件,通过SSH协议与远程服务器进行连接,去操控一系列的命令信息。就像......
  • 【全网最全】2024年数学建模国赛C题保奖思路+成品论文+matlab/python代码等(后续会更新
    您的点赞收藏是我继续更新的最大动力!一定要点击末文的卡片,那是获取资料的入口!解题思路数据读取:使用Pandas库读取Excel文件中的数据。数据清洗:检查数据是否完整,处理可能的重复项或异常值。数据分析:基于地块类型、面积等特征进行基本的数据分析,例如统计每种地块类型的总面积......
  • PKUSC 2024 Day1 t3 独立
    想到了一个还比较优美的做法,首先令好\(dp\)后设\(d_{x}=max(dp_{x,0},dp_{x,1})-dp_{x,0}\)后原问题可以转化为\(d_{x}=max(w_{x}-\sum_{y\inson_{x}}d_{y},0)\),最后其实就是求所有方案\(\sum_{i=1}^{n}d_{i}\)的和,由于每一个点的期望只与子树有关而与子树外无关,直接对子......
  • 章10——面向对象编程(高级部分)——final关键字
    基本介绍注意事项final修饰不同东西属性:相当于常量,需要赋初值(构造器(除static)、代码块、定义时)。构造器不可以是静态的,因为构造器中隐含了super和this。类:不可继承。方法:不可重写,但可继承。因为不可以重写的特质不可以修饰构造方法。final和static搭配效率高是因为:不会导......