AtCoder ABC 250 总结

总体

连续若干次一样的结果:30min 切前 4 题,剩下卡在 T5

这几次卡在 T5 都是一次比一次接近,

什么 dp 前缀和打挂,精度被卡,能水过的题连水法都没写出来

确实是思维上的缺陷,毕竟 atc 全程都是紧张的

等下一场吧。。

前 4 题较易,不再赘述。

E- Prefix Equality

长度为 \(n\) 的数组 \(A,B\) ,每次询问 \(A\) 的前 \(x\) 项和 \(B\) 的前 \(y\) 项组成的集合是否相同

  • 即排序、去重后判断相等

\(n,Q\le 2\times 10^5\) , \(a_i,b_i\le 10^9\)

考场水法

机房其他大佬都这样做。

大力莫队,记录一个变量 \(now\) ,两个桶记录

若移动指针时,把数加入两个桶

若加入这个数,出现矛盾,则 \(now\leftarrow now-1\) ,若满足条件,则 \(now\leftarrow now+1\)

  • 矛盾:指一个数只在一个数组中出现

对于询问,\(now=0\) 时是 Yes ,否则 No

  • 若两个前缀相同,则所有位置矛盾和满足条件的贡献抵消。最终 \(now=0\)

由于不保证 \(x<y\) ,所以复杂度也无法保证。

但是,大力出奇迹。

code

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 4e5 + 5;
int n, a[N], b[N], c[N], le, Ti, t[N][2], Bsz, now;
bool ans[N];
struct qry { int x, y, id; } q[N];
inline bool cmp(qry A, qry B) {if (A.x / Bsz != B.x / Bsz) return A.x / Bsz < B.x / Bsz;if ((A.x / Bsz) & 1) return A.y < B.y;return A.y > B.y;
}
inline void add(int x, int o) {if (!t[x][o]) {if (t[x][o ^ 1]) ++now;else --now;}++t[x][o];
}
inline void del(int x, int o) {--t[x][o];if (!t[x][o]) {if (t[x][o ^ 1]) --now;else ++now;}
}
inline void solve() {int X = 0, Y = 0;for (int i = 1, qx, qy; i <= Ti; i++) {qx = q[i].x, qy = q[i].y;while (X < qx) add(a[++X], 0);while (X > qx) del(a[X--], 0);while (Y < qy) add(b[++Y], 1);while (Y > qy) del(b[Y--], 1);ans[q[i].id] = (now == 0);}
}
int main() {scanf("%d", &n);Bsz = sqrt(n);for (int i = 1; i <= n; i++) scanf("%d", &a[i]), c[++le] = a[i];for (int i = 1; i <= n; i++) scanf("%d", &b[i]), c[++le] = b[i];sort(c + 1, c + le + 1);le = unique(c + 1, c + le + 1) - c - 1;for (int i = 1; i <= n; i++) {a[i] = lower_bound(c + 1, c + le + 1, a[i]) - c;b[i] = lower_bound(c + 1, c + le + 1, b[i]) - c;}scanf("%d", &Ti);for (int i = 1; i <= Ti; i++) scanf("%d%d", &q[i].x, &q[i].y), q[i].id = i;sort(q + 1, q + Ti + 1, cmp);solve();for (int i = 1; i <= Ti; i++) puts(ans[i] ? "Yes" : "No");
}

正解

part 1

首先明确(我考场也想到了):对于每一个 \(x\),满足条件的 \(y\) 是在一个区间 \([L_x,R_x]\) 里的。

所以考虑求 \(L_x,R_x\) 。

离散化是自然的。

接着记录 \(A\) 中元素在 \(B\) 中第一次出现位置 \(p\) ,即 \(p_{a_i}=\min\{j\mid b_j=a_i\}\)

设 \(S_i\) 为 \(A\) 前 \(i\) 项的集合,有 \(S_0=\varnothing\) 。可得

\[L_i=\max_{t\in S_i} p_t,R_i=\min_{t\in S_n-S_i} p_t-1 \]

其中 \(S-T=\{a\mid a\in S,a\notin T\}\)

可以用前缀最大值、后缀最小值 \(O(n)\) 计算,\(O(1)\) 回答询问。

复杂度为离散化的复杂度 \(O(n\log n)\)

part 2

更具体地实现:记录 \(add_i\) 表示 \(A_i\) 在 \(S_i\) 中是否第一次出现

\(L_i\) 可以直接求,求 \(R_i\) 需要注意:

如果 \(B\) 中有 \(A\) 中没有的元素,若这个位置最小为 \(pos\) ,则 \(R\) 都要要小于 \(pos\)

可以记录 \(suf_i\) 为后缀最小值

\[suf_i=\min_{t\in S_n-S_{i-1}} p_t-1 \]

从后往前扫,若 \(add_i=1\) ,则说明 \(a_i\notin S_{i-1}\) ,可用 \(p_{a_i}-1\) 更新 \(R_i\)

从前往后扫,通过 \(suf\) 数组和 \(pos\) 求出 \(R\) 数组

code

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 4e5 + 5;
int n, a[N], b[N], c[N], le, Ti, L[N], R[N];
int pos[N], vis[N], add[N], mn[N], tmn;
bool ans[N];
int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%d", &a[i]), c[++le] = a[i];for (int i = 1; i <= n; i++) scanf("%d", &b[i]), c[++le] = b[i];sort(c + 1, c + le + 1);le = unique(c + 1, c + le + 1) - c - 1;for (int i = 1; i <= n; i++) {a[i] = lower_bound(c + 1, c + le + 1, a[i]) - c;b[i] = lower_bound(c + 1, c + le + 1, b[i]) - c;}for (int i = 1; i <= n; i++) if (!pos[b[i]]) pos[b[i]] = i;for (int i = 1; i <= n; i++) if (!pos[a[i]]) pos[a[i]] = n + 1;for (int i = 1; i <= n; i++) if (!vis[a[i]]) vis[a[i]] = 1, add[i] = 1;for (int i = 1; i <= n; i++) {L[i] = L[i - 1];if (add[i]) L[i] = max(L[i], pos[a[i]]);}mn[n + 1] = n + 1;for (int i = n; i; i--) {mn[i] = mn[i + 1];if (add[i]) mn[i] = min(mn[i], pos[a[i]]);}tmn = n + 1;for (int i = n; i; i--) if (!vis[b[i]]) tmn = min(tmn, i);for (int i = 1; i <= n; i++) {if (!add[i]) R[i] = R[i - 1];else R[i] = min(mn[i + 1], tmn) - 1;}scanf("%d", &Ti);for (int i = 1, x, y; i <= Ti; i++) {scanf("%d%d", &x, &y);puts(L[x] <= y && y <= R[x] ? "Yes" : "No");}
}

F - One Fourth

一个 \(n\) 个点的凸多边形,逆时针给出这 \(n\) 个点 \(P_i=(x_i,y_i)\) 。

设这个凸多边形面积为 \(S\) ,现在要选出两个不相邻的点,

沿两点所在直线将凸多边形切成两部分,

选出一部分,设其面积为 \(b\) ,求 \(\min 8\times|\dfrac{1}{4}S-b|\)

\(n\le 10^5\) ,\(|x_i|,|y_i|\le 4\times 10^8\)

sol

  • 转换所求:\(8\times|\dfrac{1}{4}S-b|=|2S-8b|=|2S-4\times 2b|\)

  • 求面积:用向量叉乘,求得两个向量所形成的平行四边形面积,

    \[S=\dfrac{1}{2}\sum_{i=2}^N\overrightarrow{P_1P_{i-1}}\times\overrightarrow{P_1 P_i} \]
  • 凸多边形是一个环,此处定义点 \(N+i\) 为点 \(i\) ,即破环成链

  • \(O(n^2)\) :很显然对于 \(p\) 枚举 \(q=(p+1),(p+2),\cdots,(p+N-2)\) ,

    每次直接加上 \(p,q,(q+1)\) 围成的三角形的面积,可以做到 \(n^2\)

  • \(O(n)\) :用到双指针:对于每个 \(p\) ,若当前面积 \(now<\dfrac{S}{4}\) ,则 \(q\) 一直加,加上 \(p,q,(q+1)\) 的面积

    然后 \(p\) 后移,减去 \(p,(p+1),q\) 的面积。

  • 实现时:可以不用真的破环成链。由于转换,求面积时的 \(\dfrac{1}{2}\) 都可以消掉

code

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 1e5 + 5;
int n;
struct P {LL x, y;P(LL _x = 0, LL _y = 0) : x(_x), y(_y) { }P operator + (P A) { return P(x + A.x, y + A.y); }P operator - (P A) { return P(x - A.x, y - A.y); }
} a[N];
LL sm, ans = 9e18, nw;
inline LL mul(P A, P B) { return A.x * B.y - A.y * B.x; }
// A is origin
// vector AB and AC
inline LL area(P A, P B, P C) { return abs(mul(B - A, C - A)); }
int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%lld%lld", &a[i].x, &a[i].y);for (int i = 3; i <= n; i++) sm += area(a[1], a[i - 1], a[i]);for (int i = 1, j = 2; i <= n; i++) {while (4 * nw < sm) {nw += area(a[i], a[j], a[j % n + 1]);j = j % n + 1;ans = min(ans, abs(sm - 4 * nw));}nw -= area(a[j], a[i], a[i % n + 1]);ans = min(ans, abs(sm - 4 * nw));}printf("%lld", ans);
}

G - Stonks

作 \(n\) 天股票交易,初始有无限钱,第 \(i\) 天股票价格为 \(P_i\) ,一天进行以下操作之一

  • 买进股票,花费 \(P_i\) 元
  • 卖出股票,增加 \(P_i\)
  • 什么也不做

求最大利润

\(n\le 2\times 10^5\)

sol

反悔贪心,思路巧妙

遇到当前价格高于最低股票价时就卖掉,

如果之后有一个更高价格的呢,就买入此时卖出的,达到反悔的效果

即对于 \(a<b<c\) ,以 \(a\) 的价格买入,以 \(b\) 的价格贪心卖出,再以 \(b\) 的价格买入,\(c\) 的价格卖出

就相当于 \(a\) 的价格买入, \(c\) 的价格卖出,实现了反悔

用优先队列实现

code

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 2e5 + 5;
int n;
LL a[N], ans;
priority_queue<LL, vector<LL>, greater<LL> > Q;
int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);Q.push(a[1]);for (int i = 2; i <= n; i++) {if (Q.top() < a[i]) {ans += a[i] - Q.top();Q.pop(), Q.push(a[i]);}Q.push(a[i]);}printf("%lld", ans);
}

Ex - Trespassing Takahashi

\(n\) 点 \(m\) 边的无向图,点 \(1,2,\cdots,K\) 为房子,第 \(i\) 条边连接 \(a_i,b_i\) ,通过时间为 \(c_i\)

保证原图是联通的,无重边、自环

\(Q\) 次询问,第 \(i\) 次询问 \(x_i,y_i,t_i\) ,问能否从房子 \(x_i\) 到房子 \(y_i\) ,移动过程中离开房子的时间不能大于 \(t_i\)

\(K\le n\le 2\times 10^5\) ,\(Q,m\le 2\times 10^5\) , \(1\le t_1\le\cdots\le t_Q\le 10^{15}\) , \(c_i\le 10^9\)

sol

另 \(d_i\) 为从点 \(i\) 到最近的房子的距离,对每条边,另 \(c'_i=c_i+d_{a_i}+d_{b_i}\)

结论:答案为 Yes 当且仅当只用 \(c'_i\le t\) 的边能从 \(x\) 到 \(y\)

证明如下:

  1. 首先,\(c'_i\) 表示必须经过边 \(i\) 从一个房子到另一个房子的最短距离

    如果不能只用 \(c'_i\le t\) 的边从房子 \(x\) 到房子 \(y\) ,答案是否定的

  2. 另外,如果可以只用满足 \(c'_i\le t\) 的路径从 \(x\) 到 \(y\)

    若 \(p_1,\cdots,p_k\) 为 \(x\) 到 \(y\) 路径上的点,考虑路径

    \[p_1,(\text{Nearest house from }p_1),p_1, p_2,(\text{Nearest house from }p_2),p_2,\cdots,p_k \]

    其中

    \[(\text{Nearest house from }p_i),p_i, p_{i+1},(\text{Nearest house from }p_{i+1}) \]

    之间的长度等于连接 \(p_i,p_{i+1}\) 的 \(c'\) 边,是满足条件的。

    所以这次询问的答案是 Yes

\(d_i\) 可以用有 \(K\) 个起点的 dijkstra 在 \(O(n\log n)\) 的时间复杂度内解决。

由于 \(t\) 单调不减,将所有 \(c'_i\) 排序,每次将满足 \(c'_i\le t\) 的边加入 DSU ,判断 \(x,y\) 是否联通即可

code

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 2e5 + 5;
int n, m, lst[N], Ecnt, K, ff[N], vis[N], Ti;
LL dis[N];
struct Ed { int to, nxt; LL qz; } e[N << 1];
struct Edge { int u, v; LL w; } ed[N];
inline void Ae(int fr, int go, int vl) {e[++Ecnt] = (Ed){ go, lst[fr], 1ll * vl }, lst[fr] = Ecnt;
}
int fd(int x) { return ff[x] == x ? x : ff[x] = fd(ff[x]); }
priority_queue< pair<LL, int> > Q;
inline bool cmp(Edge A, Edge B) {return A.w < B.w;
}
int main() {scanf("%d%d%d", &n, &m, &K);for (int i = 1; i <= n; i++) {dis[i] = 1e16;ff[i] = i;}for (int i = 1, u, v, w; i <= m; i++) {scanf("%d%d%d", &u, &v, &w);Ae(u, v, w), Ae(v, u, w);ed[i].u = u, ed[i].v = v, ed[i].w = w;}for (int i = 1; i <= K; i++) {dis[i] = 0;Q.push(make_pair(0, i));}while (!Q.empty()) {register int u = Q.top().second;Q.pop();if (vis[u]) continue;vis[u] = 1;for (int i = lst[u], v; i; i = e[i].nxt)if (dis[u] + e[i].qz < dis[v = e[i].to]) {dis[v] = dis[u] + e[i].qz;Q.push(make_pair(-dis[v], v));}}for (int i = 1; i <= m; i++) ed[i].w += dis[ed[i].u] + dis[ed[i].v];sort(ed + 1, ed + m + 1, cmp);scanf("%d", &Ti);for (int x, y, p, q, i = 1; Ti--; ) {register LL t;scanf("%d%d%lld", &x, &y, &t);while (i <= m && ed[i].w <= t) {p = fd(ed[i].u), q = fd(ed[i].v);if (p ^ q) ff[p] = q;i++;}puts(fd(x) == fd(y) ? "Yes" : "No");}
}

AtCoder ABC 250 总结相关推荐

  1. Atcoder abc 233 题解

    Atcoder abc 233 题解 A 10yen Stamp 答案就是两个数字的差/10之后上取整 记得判断res=0的情况就可以了 c++代码 #include<iostream> ...

  2. AtCoder ABC 127F Absolute Minima

    题目链接:https://atcoder.jp/contests/abc127/tasks/abc127_f 题目大意 初始状态下$f(x) = 0$,现在有 2 种模式的询问,第一种以"1 ...

  3. Atcoder abc A~E

    ABC 265 A~E A 题意:你要买 n 个苹果,买 1 个要 x 元,买 3 个要 y 元,问最少要花多少钱 解法:判断 3*x 是否小于 y,然后输出 Code : # include < ...

  4. AtCoder - ABC 167 - E(数学推理+组合数)

    E - Colorful Blocks 题意: 有 m 种颜色,给 n 个方块染色,可以不使用所有颜色,要求最多有 k 对相邻方块同色.问染色的总情况,最终结果模  998244353. 数据范围: ...

  5. AtCoder ABC 249

    A Jogging 没看清楚题 a不是移速 所以卡了十分钟 语法题 int main() {int a, b, c, d, e, f, x;cin >> a >> b > ...

  6. AtCoder ABC 128(C ~ E)

    C Switches(枚举 + 位运算) Description: ​ 有n盏灯和m组设定,要求输出同时符合m组设定的灯开关的方案数 ​ 设定:s_i中亮灯的数量%2 == p_i Solution: ...

  7. AtCoder - ABC 178 - C~F

    C - Ubiquity(容斥原理/DP) 题意: 求满足以下条件的长为 n 的不同序列的个数: 1.0 ≤  ≤9 2.序列中至少有一个 =0 3.序列中至少有一个 =9 答案对 +7 取模. 数据 ...

  8. AtCoder ABC 247

    A Move Right(水) 字符串右移一位 然后在左边补0 void solve() {string s;cin >> s;s = '0' + s;s.erase(s.end() - ...

  9. AtCoder - ABC 170 - D(思维+数论)E(STL+模拟)

    D - Not Divisible 题意: 现有一个长度为 n 的序列 A,请你输出满足以下条件的所有整数  的数量: 对于每一个整数 j (1 ≤ j ≤ n且 i ≠ j) ,  ≠ 0 ( mo ...

最新文章

  1. 输出内容时后面显示乱码
  2. PHP快速入门 如何操作MySQL
  3. VF02 会计凭证过账时间
  4. c字符串截取一部分字符串_Python如何截取一段字符串?
  5. python中functools_functools模块2个常用函数
  6. JEECG Excel 实体类
  7. tnsnames.ora 的编写
  8. 深入理解java虚拟机章节_深入理解java虚拟机-第六章
  9. excel使用教程_办公软件excel表格制作教程
  10. centos7中 npm install express 时Error: Cannot find module 'express'错误
  11. Java程序员博客系统推荐!我调研了100来个 Java 开源博客系统,发现这 5 个最好用!
  12. 【数据可视化】三款主流开源数据可视化工具对比:Superset、DataEase、MetaBase
  13. luogu 4234 最小差值生成树 LCT
  14. SPARK SQL ERROR: Detected cartesian product for INNER join between logical plans报错解决方法
  15. AOE网:关键路径和关键活动
  16. 轰隆隆-小站地址原理整理分析
  17. oracle计算两行差值
  18. note edge android 6.0 root,三星Note Edge N9150刷机包带ROOT权限固件ZCU1BOH2线刷Rom
  19. 四:MySQL 表介绍
  20. WLS(适用于Windows的Linux子系统)的安装

热门文章

  1. 简历中的“自我评价“怎么写?记录一个满分模板
  2. 苏宁精准营销之生成人群包的演进
  3. 我的2013,它不平淡
  4. 《财富》封面文章:重新审视世界500强,它们又卷土重来了
  5. pico的学习之路(二)——YL-56声音传感器(树莓派pico实现)
  6. 专升本计算机的数学考不考正态分布,高考成绩不一定是正态分布
  7. TS协议解析第三部分(PES)
  8. Security Shepherd实战笔记(答案)
  9. 小记 百度地图 soso地图 经纬度偏移
  10. [DB] From Leng,Oracle 数据库报ora-653 ora-01654错误解决办法