abc309f <线段树 + 离散化 + 双指针>
1.abc306e <mutiset的使用>2.abc053d <简单贪心>3.abc042d <组合数>4.abc043d5.abc045d6.abc044d <根号分治>7.abc046d8.abc047d9.abc048d <博弈>10.abc049d <并查集>11.abc050d <???>12.abc051 <多源最短路>13.abc052d14.abc054d <dp, 背包>15.abc055d <枚举>16.abc056d <贪心 / 二分+DP+bitset>17.abc057d <贪心+组合计数>18.abc058d <公式化简>19.abc059d <博弈, 打表找规律>20.abc060d <dp, 背包>21.abc061d <单源最短路, spfa, 判断负环>22.abc062d <优先队列>23.abc063d <二分答案>24.abc064d <贪心/前缀和>
25.abc309f <线段树 + 离散化 + 双指针>
26.abc309e <dfs>27.abc065d <贪心+最小生成树> [lambda表达式]28.abc066d <组合>29.abc067d <博弈 + dfs>30.abc068d <思维 + 构造>31.abc069d <构造>32.abc070d <简单树上dfs>33.abc071d <递推>34.abc072d <贪心>35.abc073d <Floyed + 枚举排列>36.abc074d <Floyed 消除传递边>37.abc075d <暴力枚举 / 枚举+离散化+二维前缀和>38.abc076d <dp / 贪心>39.abc077d <思维 + 最短路 (将构造数字过程视为最短路)>40.abc078d <博弈>41.abc079d <Floyed>42.abc080d <区间重叠>43.abc081d <思维 构造>44.abc082d <bitset 状压dp>45.abc083d <思维 贪心>46.abc084d <素数筛 前缀和>47.abc085d <贪心>48.abc086d <二维前缀和 同余>49.abc087d <并查集 维护距离信息>50.abc088 <bfs 最短路>51.abc089 <前缀和>52.abc310d <dfs暴搜-分组方案数 / bitmask表示集合+dp>53.abc310e <公式递推(dp?)>54.abc310f <dp + bitmask>55.abc090d <枚举计数>56.abc312c <二分答案>57.abc312d <dp, 括号匹配方案数>58.abc312e <暴力>59.abc320f <dp >60.abc092d<构造,思维>61.abc094d<组合数>62.abc095d<思维>63.abc096d<素数筛,整除>64.abc097d<并查集,排列>65.abc098d<双指针,异或>66.abc099d<dfs,枚举排列方案>67.abc100d<枚举>68.abc101d<打表,数学>// https://atcoder.jp/contests/abc309/tasks/abc309_f
// <线段树 + 离散化 + 双指针> [unique + lower_bound + erase + lambda + vector]
// 总体思路: 将每个三元组记录为如 a[3] 的3维向量, 依次考虑每个向量, 检查是否存在一个向量完全比它'小'
// 将向量按照第一维度 a[0] 从小到大排序, 则当考虑第i个向量时, 可能比它'小'的仅可能是前i-1个向量; 如此处理第一维度;
// 至于第二三维度, 使用线段树维护前i-1个向量的情况,
// 其中线段树下标u为第二维度, 此维度需要先进行离散化,
// tr[u].val 为前i-1个向量中, 第二维度为u(离散化后索引)的向量, 第三维度的最小值为 val;
// 如此, 当前第i个向量为a, 其第二维度离散化后为t1=find(a[1]), 则前i-1个向量中, 第二维度比a小的向量在线段树中下标为 1~t1-1,
// 因此可查询query(1, 1, t1-1), 得到前i-1个向量(由于事先排序, 因此满足第一维度小于a)中, 第二维度小于t1(向量a离散化后的索引), 的第三维度的最小值,
// 如果此查询值小于a的第三维度a[2], 则Yes;
// 当完成第一位度相同若干向量的检查后, 将这些向量更新到线段树中...
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
using LL = long long;
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
struct Node
{
int l, r, val;
}tr[N*3*4]; // 1. 注意 *3是每个向量有3个维度, *4是线段数要求
void push_up(int u)
{
tr[u].val = min(tr[u<<1].val, tr[u<<1|1].val);
}
void build(int u, int l, int r)
{
if (l == r)
{
// tr[u].val = INF;
// tr[u].l = tr[u].r = l;
tr[u] = {l, r, INF}; // 2. 习惯这种结构体赋值方式, 更简洁(注意顺序)
}
else
{
tr[u].l = l; tr[u].r = r;
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
push_up(u);
}
}
int query(int u, int l, int r)
{
// 3. 将线段数写成结构体的形式而非数组似乎更简洁
if (l > tr[u].r || r < tr[u].l) return INF; // 4. 将越界写在前面, 这样下面就不需要讨论范围了
if (l <= tr[u].l && r >= tr[u].r) return tr[u].val;
int mid = tr[u].l + tr[u].r >> 1;
int res = min(query(u<<1, l, r), query(u<<1|1, l, r));
return res;
}
void modify(int u, int x, int val)
{
if (tr[u].l > x || tr[u].r < x) return; // 将越界写在前面, 这样下面就不需要讨论范围了 (if x <= mid ...)
if (tr[u].l == tr[u].r)
{
tr[u].val = min(tr[u].val, val);
return;
}
modify(u<<1, x, val); modify(u<<1|1, x, val);
push_up(u);
}
void solv()
{
int n;
cin >> n;
vector<vector<int>> a(n+1, vector<int>(3)); // 5. 习惯这种写法, 确实好用
vector<int> temp;
for (int i = 1; i <= n; i ++)
{
for (int j = 0; j < 3; j ++)
{
cin >> a[i][j];
temp.push_back(a[i][j]);
}
sort(a[i].begin(), a[i].end()); // 6. 向量3个维度升序排序
}
temp.push_back(0); // 7. 前面补0, 使得离散化后下标从1开始, 方便线段树的下标
// 8. 离散化常用操作, 排序+去重+二分查找
sort(temp.begin(), temp.end());
temp.erase(unique(temp.begin(), temp.end()), temp.end());
auto find = [&] (int x) -> int {
return lower_bound(temp.begin(), temp.end(), x) - temp.begin(); // 9. lambda写法, 注意末尾分号
};
sort(a.begin(), a.end(), [&] (vector<int> &a, vector<int> &b) { // 10. 简洁的sort ~
return a[0] < b[0];
});
build(1, 1, temp.size()-1);
for (int i = 1; i <= n; i ++) // 按向量第一维度, 从小到大考虑
{
int j = i;
while (j <= n && a[i][0] == a[j][0]) // j 指针遍历所有第一维相同的向量
{
int t1 = find(a[j][1]); // t1 获得离散化后的索引
if (query(1, 1, t1-1) < a[j][2]) // 如果前i-1个第一维满足要求的向量中, 存在第二三维度也满足要求的向量
{
cout << "Yes" << endl;
return;
}
j ++;
}
for (int k = i; k < j; k ++) // 将本轮遍历到的第一维度相同的向量更新到线段树
{
int t1 = find(a[k][1]);
modify(1, t1, a[k][2]);
}
i = j-1;
}
cout << "No" << endl;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while (T --)
{
solv();
}
return 0;
}
本文来自博客园,作者:O2iginal,转载请注明原文链接:https://www.cnblogs.com/o2iginal/p/17538524.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律