首页 > 其他分享 >2024-10-10 模拟赛总结

2024-10-10 模拟赛总结

时间:2024-10-15 12:47:25浏览次数:7  
标签:10 int long 2024 ++ kMaxN 端点 模拟 first

\(100+100+0+20=220\),部分分还是没有拿满。

比赛链接:http://172.45.35.5/d/HEIGETWO/homework/6707886f6735d3863dc8c0ef
http://yl503.yali.edu.cn/d/HEIGETWO/homework/6707886f6735d3863dc8c0ef

A - 植物收集 / collect

题意:

你要收集 \(n\) 个阶段的植物,你可以选择花费 \(a_i\) 的价格买到一个阶段的植物,你还可以花费 \(k\) 的代价使当前所有的植物的阶段加一,若一个植物阶段为 \(n\),那么其阶段会变成 \(1\),求收集到所有 \(n\) 个阶段的植物的最小代价。

思路:

首先可以发现一个显然的性质,我们考虑将所有植物单个使用技能,那么多个植物使用的最少技能次数为单个使用技能次数的最大值。所以我们可以枚举使用了 \(k\) 次技能,那么单个植物可以使用的最多技能次数为 \(k\),那么就是在当前位置的前 \(k\) 个找到 \(a\) 的最小值,这可以用单调队列维护,这样就实现了时间复杂度为 \(O(n^2)\) 的做法。通过打表可以知道最小代价关于使用技能次数的函数是一个开口朝上的单峰函数,可以用二分寻找最小值,时间复杂度 \(O(n\log n)\),可以通过此题。

代码:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 1e5 + 5;

int n, k, a[kMaxN * 2], q[kMaxN * 2], h, t, L, R, M;
long long ans = 1e18, f[kMaxN];

long long C(int l, long long ret = 0) {
  if (f[l]) return f[l];
  h = 1, t = 0;
  for (int i = n - l + 1; i <= n; i++) {
    for (; h <= t && a[q[t]] >= a[i]; t--) {
    }
    q[++t] = i;
  }
  for (int i = n + 1; i <= n + n; i++) {
    for (; h <= t && a[q[t]] >= a[i]; t--) {
    }
    q[++t] = i, ret += a[q[h]];
    for (; h <= t && q[h] <= i - l; h++) {
    }
  }
  return f[l] = ret + 1LL * l * k;
}

int main() {
  freopen("collect.in", "r", stdin);
  freopen("collect.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> k, f[n + 1] = 1e18;
  for (int i = 1; i <= n; i++) {
    cin >> a[i], a[n + i] = a[i];
  }
  L = 0, R = n, M = L + R >> 1;
  for (; L < R; M = L + R >> 1) {
    C(M + 1) >= C(M) ? R = M : L = M + 1;
  }
  cout << C(L);
  return 0;
}

B - 美丽子区间 / interval

题意:

定义一个子区间是美丽的,当且仅当这个子区间的左端点和右端点都不是这个子区间的最大值或最小值,给定一个长度为 \(n\) 的排列 \(p\),求 \(p\) 有多少个子区间是美丽的。

思路:

我们考虑一个数能成为美丽子区间的右端点,这个区间的左端点有什么限制,那么我们就需要找到这个数的从右到左数第一个比它大的和第一个比他小的数,这两个位置取 \(\min\),就是左端点需要在这个数的左边(不包括这个数)。同理,那么对于每一个数它要成为左端点,右端点也有一段区间。我们设 \(l_i\) 表示以编号为 \(i\) 的数为右端点,区间左端点至少要左到哪里,\(r_i\) 表示一边好为 \(i\) 的数为左端点,区间右端点至少要右到哪里,\(l_i,r_i\) 可以用单调栈或者权值树状数组求。

我们枚举以 \(i\) 为右端点,那么只有 \(1\) 到 \(l_i\) 的数可以成为区间的左端点,但是还要保证这些数的 \(r_i\) 小于 \(i\),这可以将问题离线,然后用权值树状数组处理。(在线的话好像也可以用主席树做)

代码:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 2e5 + 5;

int n, a[kMaxN], c[2][kMaxN], l[kMaxN], r[kMaxN];
long long ans;
vector<int> v[kMaxN];

void modify(int x, int y, int id, int o) {  // o : [0 : min / 1 : max / 2 : +]
  for (int i = x; i <= n; i += i & -i) {
    c[id][i] = o == 2 ? c[id][i] + y : o ? max(c[id][i], y) : min(c[id][i], y);
  }
}

int query(int x, int id, int o) {
  int r = o == 2 ? 0 : o ? -1e9 : 1e9;
  for (int i = x; i; i -= i & -i) {
    r = o == 2 ? r + c[id][i] : o ? max(c[id][i], r) : min(c[id][i], r);
  }
  return r;
}

int main() {
  freopen("interval.in", "r", stdin);
  freopen("interval.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  memset(c[0], 0xc0, sizeof(c[0])), memset(c[1], 0xc0, sizeof(c[1]));
  for (int i = 1; i <= n; i++) {
    l[i] = min(query(n + 1 - a[i], 0, 1), query(a[i], 1, 1)) - 1, modify(n + 1 - a[i], i, 0, 1), modify(a[i], i, 1, 1);
  }
  memset(c[0], 0x3f, sizeof(c[0])), memset(c[1], 0x3f, sizeof(c[1]));
  for (int i = n; i; i--) {
    r[i] = max(query(n + 1 - a[i], 0, 0), query(a[i], 1, 0)) + 1, modify(n + 1 - a[i], i, 0, 0), modify(a[i], i, 1, 0);
  }
  for (int i = 1; i <= n; i++) {
    if (l[i] > 0) v[l[i]].push_back(i);
  }
  memset(c[0], 0, sizeof(c[0]));
  for (int i = 1; i <= n; i++) {
    if (r[i] <= n) modify(r[i], 1, 0, 2);
    for (int j : v[i]) {
      ans += query(j, 0, 2);
    }
  }
  cout << ans;
  return 0;
}

C - 字符序列 / subseq

题意:

定义一个函数 \(f(c,s)=cs_1cs_2\cdots cs_{|s|}c\)(表示拼接),给定一个长度为 \(n\) 的字符串 \(c\),\(s_0\) 为空串,\(s_i=f(c_i,s_{i-1})\),求 \(s_n\) 的本质不同子序列的个数。

思路:

首先我们考虑如何快速求出一个本质不同子序列的个数。考虑 dp。

第一种做法:

我们令 \(f_i\) 为选到第 \(i\) 位的本质不同子序列的个数。

  • 若在 \(i\) 之前没有出现过 \(s_i\),那么 \(f_i=2\times f_{i-1}+1\),可以在前 \(i-1\) 的子序列中加上当前字符或者不加所以要乘 \(2\),加一是因为要算自己单个成子序列。
  • 若在 \(i\) 之前出现过 \(s_i\),那么 \(f_i=2\times f_{i-1}-f_{last_{s_i}-1}\),\(last_{s_i}\) 表示 \(s_i\) 上一次出现的位置减去的原因是它如果加上当前字符就会算重,不需要加一是因为包括在 \(f_{i-1}\) 中。

这种做法的时间复杂度是 \(O(n)\) 的,这种做法的优点是与字符集大小无关,缺点是不能矩阵乘法。

第二种做法:

我们令 \(f_{i,j}\) 表示选到第 \(i\) 位,结尾为 \(j\) 的本质不同子序列的个数。

  • 若 \(j\neq s_i\),那么 \(f_{i,j}=f_{i-1,j}\)。
  • 若 \(j= s_i\),那么 \(f_{i,j}=\sum_{k\in V} f_{i-1,k}\),\(V\) 为字符集。
    • 若 \(k\neq s_i\),那么可以在最后加上一个字符 \(s_i\)。
    • 若 \(k=s_i\),那么可以选用前面的结尾为 \(s_i\) 的子序列,不需要加上 \(s_i\)。

这种做法的时间复杂度是 \(O(n\times |V|)\),这种做法的优点是可以矩阵乘法,缺点是与字符集大小有关。

这道题我们使用第二种做法,因为它可以矩阵乘法。

我们观察这个字符串是如何生成的,例如 \(c=\texttt{abcd}\),那么:

\[s_1=\texttt a \]

\[s_2=\texttt{bab} \]

\[s_3=\texttt{cbcacbc} \]

\[s_4=\texttt{dcdbdcdadcdbdcd} \]

观察 \(s_4\),可以发现它是一个回文串,且回文串的两边也是回文串,所以我们可以分治做,用分治求出转移矩阵的乘积,用矩阵乘法即可。

代码:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 505, kM = 1e9 + 7;

int n;
char c[kMaxN];
long long ans;

struct M {
  int n, m;
  long long a[30][30];

  M(int h = 0, int w = 0) { n = h, m = w, memset(a, 0, sizeof(a)); }
  M(char c) {
    n = m = 27, memset(a, 0, sizeof(a)), a[26][26] = 1;
    for (int j = 0; j < 26; j++) {
      if (j != c - 'a') {
        a[j][j] = 1;
      } else {
        for (int i = 0; i < 27; i++) {
          a[i][j] = 1;
        }
      }
    }
  }

  long long* operator[](int x) { return a[x]; }

  M operator*(M b) const {
    M r(n, b.m);
    for (int i = 0; i < n; i++) {
      for (int k = 0; k < m; k++) {
        for (int j = 0; j < b.m; j++) {
          r[i][j] = (r[i][j] + a[i][k] * b[k][j]) % kM;
        }
      }
    }
    return r;
  }
} F(1, 27), Ft(27, 27);

int main() {
  freopen("subseq.in", "r", stdin);
  freopen("subseq.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> c + 1, F[0][26] = 1;
  for (int i = 0; i < 27; i++) {
    Ft[i][i] = 1;
  }
  for (int i = n; i; i--) {
    Ft = Ft * M(c[i]) * Ft;
  }
  F = F * Ft;
  for (int i = 0; i < 26; i++) {
    ans = (ans + F[0][i]) % kM;
  }
  cout << ans;
  return 0;
}

D - 网络攻防 / attack

题意:

给定一个 \(n\) 个点 \(m\) 条边的无向连通图,求删除至多 \(k\) 条边使图不连通的方案数。(\(k\in \{1,2\}\))

思路:

首先当 \(k=1\),就是求割边数量,可以用 tarjan 求,也可以用 dfs 树和树上差分求,简单讲一下第二种做法。

首先构建出 dfs 树,由于是无向图,所以只有返祖边,没有被返祖边覆盖的边就是桥,我们可以用树上差分求出每一条边被覆盖几次。

现在考虑 \(k=2\),只需要求删除 \(2\) 条边使图不连通的方案数即可,开始分类讨论。(下面的边的覆盖集,指树边被非树边覆盖的集合)

  • 这两条边都是非树边,这显然不能使图不连通。
  • 这两条边一条是桥,一条是非树边,显然会使图不连通。
  • 这两条边一条是普通树边,一条是非树边,若这条普通树边的覆盖集中有且仅有这条非树边,那么就肯定能使图连通,这种情况我们可以求出有多少条树边只被一条非树边覆盖即可。
  • 这两条边一条是桥,一条是桥,显然可以使图不连通。
  • 这两条边一条是桥,一条是普通树边,显然也可以使图不连通。
  • 这两条边一条是普通树边,另一条也是普通树边,举几个例子,可以发现一个结论就是若这两条边的覆盖集相等,那么可以使图不连通。判断集合是否相等,可以用随机化和哈希判断。

代码:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 1e5 + 5, kMaxM = 2e5 + 5;

int n, m, k, cnt1, cnttree, cntnottree, cntbridge;
long long ans, r[kMaxM], d[kMaxN], d2[kMaxN], sum[kMaxN], cnt[kMaxN];
mt19937 mrand(time(0) + 3247);
bitset<kMaxN> visp;
bitset<kMaxM> vise, istree, isbridge;
vector<pair<int, int>> e[kMaxN], g[kMaxN];
map<long long, int> mp;

void dfs(int u) {
  visp[u] = 1;
  for (pair<int, int> i : e[u]) {
    long long w = r[i.first];
    int v = i.second;
    if (!visp[v]) {
      vise[i.first] = 1, istree[i.first] = 1, g[u].push_back({i.first, v}), g[v].push_back({i.first, u}), dfs(v);
    } else if (!vise[i.first]) {
      vise[i.first] = 1, d2[u]++, d2[v]--, d[u] += w, d[v] -= w;
    }
  }
}

void dfs1(int u, int fa) {
  sum[u] = d[u], cnt[u] = d2[u];
  for (pair<int, int> i : g[u]) {
    int v = i.second;
    if (v != fa) {
      dfs1(v, u), sum[u] += sum[v], cnt[u] += cnt[v], cnt1 += cnt[v] == 1, cntbridge += isbridge[i.first] = !sum[v], cnttree += istree[i.first], mp[sum[v]]++;
    }
  }
}

int main() {
  freopen("attack.in", "r", stdin);
  freopen("attack.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> k;
  for (int i = 1, u, v; i <= m; i++) {
    cin >> u >> v, r[i] = uniform_int_distribution<long long>(1e9, 1e12)(mrand), e[u].push_back({i, v}), e[v].push_back({i, u});
  }
  dfs(1), dfs1(1, 0), cntnottree = m - cnttree;
  if (k == 2) {
    ans += 1LL * cntbridge * cntnottree + cnt1 + 1LL * cntbridge * (cnttree - cntbridge) + 1LL * cntbridge * (cntbridge - 1) / 2, mp[0] = 0;
    for (pair<long long, int> i : mp) {
      ans += 1LL * i.second * (i.second - 1) / 2;
    }
  }
  cout << cntbridge + ans;
  return 0;
}

标签:10,int,long,2024,++,kMaxN,端点,模拟,first
From: https://www.cnblogs.com/lrx-blogs/p/18463120

相关文章

  • 推荐10个Linux下的防病毒软件
    推荐10个Linux下的防病毒软件原创北京二锅头运维网工 2024年09月28日09:40重庆3人听过在Linux系统下,虽然由于其设计上的安全性(如权限隔离、强制访问控制等)使得病毒和恶意软件的感染率相对较低,但在某些特定场景下,如服务器环境或需要处理敏感数据的系统,安装防病毒软件仍然......
  • 【STL】模拟实现list
    目录list需要实现的接口结点类的创建迭代器类的实现构造函数++运算符的重载--运算符的重载!=运算符重载和==运算符重载operator*operator->list的模拟实现构造函数拷贝构造函数 赋值运算符重载函数析构函数迭代器相关函数begin和endfront和backpush_front......
  • dynamsoft_barcode_reader_bundle Python 10.4.2000
    RevolutionizingInventoryManagementinWarehouseswithDronesandBarcodeScanningTechnologydynamsoft_barcode_readerAsbusinessesscaleandsupplychainsbecomemorecomplex,inventorymanagementhasemergedasacriticalchallengeforwarehouseopera......
  • python-黑马程序员 初学者笔记(持续更新10.15)
    序章:由于科研室鼓励我们发布csdn,因此我们将一起学习python,这是我的笔记给大家分享出来,这不适用于一点都不会的小白,如果你看过一次或者想要回顾一下python内容再或者你正学习pyhon,可以参考本片笔记,本文章的优势在于是初学者所写,可能对于我们来说有共鸣,比较详细,并且重要知识点都......
  • 2024.10.15 1132版
    起于《海奥华预言》的思考◆地球管理结构和参考持续更新中...... 英文地址:https://github.com/zhuyongzhe/Earth/tags中文地址:https://www.cnblogs.com/zhuyongzhe85作者:朱永哲 ---------------------------------------------------------------------------------......
  • 地理信息国际标准“地理信息 室内要素模型”(ISO 19164:2024)正式发布
    近日,我国牵头制定的国际标准“地理信息室内要素模型”(Geographicinformation-Indoorfeaturemodel)由国际标准化组织正式发布,标准编号为ISO19164:2024。基本信息:标准号:ISO19164:2024EN标准名称:地理信息—室内特征模型英文名称:Geographicinformation—Indoorfeatur......
  • cvpr注意事项和注册流程(2025版)(20241015更新还未开放注册)
    本文章基于现有网上没有cvpr详细版本的一步一步的注册流程进行编写,用于指导自己和方便他人进行注册。接下来将从CVPR2025的重要节点、变更事项、注册流程进行说明重要节点CVPR2025变更的重要事项Duetothedramaticincreaseinthenumberofsubmissionsandthedeterio......
  • REXROTH DKC03.3-100-7-FW+FWA-ECODR3-FGP-03VRS-MS驱动器
    适用行业DKC03.3-100-7-FW+FWA-ECODR3-FGP-03VRS-MS型号的驱动器是一款高性能的伺服驱动器,它属于德国博世力士乐(REXROTH)品牌的产品线。这种驱动器通常集成了先进的技术,能够提供精确的速度和位置控制,适用于对动态性能要求较高的应用场景。该类驱动器适用于多种行业,特别是那些......
  • 2024软著申请详细流程分享
    软著申请全攻略:轻松掌握网上办理流程软著申请具有诸多优势,相较于专利申请更为简便,申请周期较短,且无需每年缴纳年费,对于计算机领域从业者和程序员而言是极为合适的选择。当前,软著申请既可以自行办理登记,也能够委托专业代理机构进行办理登记。其大体流程涵盖以下步骤:账号注......
  • RBE104TC C/C++ Programming Language
    RBE104TCC/C++ProgrammingLanguageAssignment1ContributiontotheOverallMarks30%IssueDateSubmissionDeadline13thOctober2024AssignmentOverview:ThisassignmentisgearedtowardsassessingfundamentalcodingconceptsinC/C++andinitiatingthep......