2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest
A. LaIS (CF contest 1468 A)
题目大意
给定数组,求最长几乎上升子数组长度。
一个有个数的数组如果满足
则数组成为几乎上升数组。
解题思路
在最长上升子序列的基础商,最长上升子序列允许中间忽然插入一个很大的数。
于是,对于第个数,它可以从前面小于的直接转移过来,或者在中插一个数较大的数。
设表示以结尾的最长几乎上升子序列的最大长度,表示以第个数结尾的最长几乎上升子序列的最大长度。
从左到右遍历,对于第个数
其中表示说位置右边第一个比它大的数的位置,这个可以事先用单调栈预处理。
表示这个数的位置。
第二种转移即为二维偏序,由于是递增的,依照更新数据即可,值得注意的是添加的时候,是要添加那个位置的最大长度值,由于已经把这个位置信息丢失了,我们需要来更新。
最值用线段树维护。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 5e5 + 8; class Segment_Tree{ #define lson root << 1 #define rson root << 1 | 1 int maxx[N << 2]; public: void build(int root, int l, int r){ if (l == r){ maxx[root] = 0; return; } int mid = (l + r) >> 1; build(lson, l, mid); build(rson, mid + 1, r); maxx[root] = max(maxx[lson], maxx[rson]); } void update(int root, int l, int r, int pos, int val){ if (l == r){ maxx[root] = max(maxx[root], val); return; } int mid = (l + r) >> 1; if (pos <= mid) update(lson, l, mid, pos, val); else update(rson, mid + 1, r, pos, val); maxx[root] = max(maxx[lson], maxx[rson]); } int query(int root, int l, int r, int ll, int rr){ if (ll <= l && r <= rr){ return maxx[root]; } int mid = (l + r) >> 1; int lans = 0, rans = 0; if (ll <= mid) lans = query(lson, l, mid, ll, rr); if (rr > mid) rans = query(rson, mid + 1, r, ll, rr); int ans = max(lans, rans); return ans; } }trans1, trans2; int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n; read(n); trans1.build(1, 1, n); trans2.build(1, 1, n); vector<int> a(n), r(n), dp1(n + 2), dp2(n + 2), id(n); iota(id.begin(), id.end(), 0); for(auto &i : a) read(i); stack<pair<int,int>> qwq; for(size_t i = 0; i < a.size(); ++ i){ while(!qwq.empty() && qwq.top().first < a[i]){ r[qwq.top().second] = i; qwq.pop(); } qwq.push({a[i], i}); } while(!qwq.empty()){ r[qwq.top().second] = n; qwq.pop(); } sort(id.begin(), id.end(), [&](int x, int y){ return r[x] < r[y]; }); int cur = 0; dp1[a[0]] = 1; dp2[0] = 1; for(size_t i = 1; i < a.size(); ++ i){ dp1[a[i]] = max({1, trans1.query(1, 1, n, 1, a[i]) + 1, trans2.query(1, 1, n, 1, a[i]) + 2}); dp2[i] = dp1[a[i]]; while(cur < n && r[id[cur]] <= (int)i){ trans2.update(1, 1, n, a[id[cur]], dp2[id[cur]]); ++ cur; } trans1.update(1, 1, n, a[i], dp1[a[i]]); } int ans = *max_element(dp1.begin(), dp1.end()); write(ans, '\n'); } return 0; }
B. Bakery (CF contest 1468 B)
题目大意
解题思路
神奇的代码
C. Berpizza (CF contest 1468 C)
题目大意
一个餐厅,两个人和。依次有顾客进来,知晓顾客消费金额。每次服务,找最先进来的顾客服务,找消费金额最高的,相同则最先进来的顾客服务。
现在依次给定发生事件序列,包括
- 顾客进来,附带估计的消费金额
- 去服务顾客
- 去服务顾客
对于第二和第三种事件,输出它们服务的顾客编号。
解题思路
顾客进来的顺序就是顾客的编号。
用一个数组标记顾客是否被服务,用优先队列维护消费金额。
对于第二种就依次找没被标记的,对于第三种就从优先队列弹出队首直到是未被服务的顾客。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int q; read(q); priority_queue<pair<int,int>> k; int l = 1; vector<bool> sign(q + 1, false); int a, b; int t = 0; while(q--){ read(a); if (a == 1) { read(b); ++ t; k.push({b, -t}); }else if (a == 2){ while(sign[l]) ++ l; sign[l] = true; write(l); }else{ while(true){ auto qwq = k.top(); k.pop(); if (sign[-qwq.second]) continue; sign[-qwq.second] = true; write(-qwq.second); break; } } } puts(""); return 0; }
D. Firecrackers (CF contest 1468 D)
题目大意
横向的格子有A和B,B抓A。B每个时刻想靠近A的方向移动一个格子,A有个鞭炮,第个鞭炮点燃后经过时刻爆炸,A每个时刻可向左或向右移动一个,或者留在原地点燃一个鞭炮。问A被抓前最多能看到多少个鞭炮爆炸。
解题思路
我们假设在左,在右。
首先不可能往右。
最优方案下,在原地不断点燃鞭炮,直到在右边一个格,此时两人再双双向左走,等待鞭炮爆炸。
模拟下这个过程就能得到答案了。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n, m, a, b; read(n); read(m); read(a); read(b); -- a; -- b; vector<int> qwq(m); for(auto &i : qwq) read(i); sort(qwq.begin(), qwq.end()); if (a > b){ a = n - 1 - a; b = n - 1 - b; } int cnt = b - a - 1; int ans = 0; int up = b; int cur = 1; int r = m - 1; while(cnt){ while(r >= 0 && cur + qwq[r] > up) -- r; if (r < 0) break; ++ ans; ++ cur; -- cnt; -- r; } write(ans, '\n'); } return 0; }
E. Four Segments (CF contest 1468 E)
题目大意
二维平面给定四条直线要求平行于坐标轴放置使得闭合矩形面积最大,求最大值。
解题思路
显然就是最短边和次长边的乘积。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int kase; cin>>kase; for (int ii = 1; ii <= kase; ii++) { LL a[4]; cin >> a[0] >> a[1] >> a[2] >> a[3]; sort(a, a + 4); cout << a[0] * a[2] <<endl; } return 0; }
F. Full Turn (CF contest 1468 F)
题目大意
个人在平面上的某点朝向某处,现在所有人以相同速度原地顺时针转,直到转了一圈。问期间有多少对人发生了对视。
解题思路
由于角速度相同,容易发现,会发生对视的那两个人它们的朝向在一条直线上,方向相反,用统计平行且反向的向量对数即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n; read(n); map<pair<LL,LL>,int> cnt; LL x, y, u, v; LL ans = 0; for(int i = 1; i <= n; ++ i){ read(x); read(y); read(u); read(v); auto qwq = __gcd(abs(u - x), abs(v - y)); // cout <<qwq << endl; auto dir = make_pair((u - x) / qwq, (v - y) / qwq); auto irdir = make_pair((x - u) / qwq, (y - v) / qwq); cnt[dir] ++; ans += cnt[irdir]; } write(ans, '\n'); } return 0; }
G. Hobbits (CF contest 1468 G)
题目大意
平面图一个分段直线函数,最右边的高处有一个眼睛,一个点从最左边沿直线走到最右边,问有多少距离,眼睛是看不到点的。
解题思路
神奇的代码
H. K and Medians (CF contest 1468 H)
题目大意
给定,个数,每次操作,选取个数(是奇数),删除除中位数之外的数。问最终能否得到给定的有个数的数组。
解题思路
首先如果不是的倍数,肯定不行。
思考删除过程,除了最后一次,期间的中位数是任意的,只要最后一次删除,中位数在数组里即可。
于是我们只要判断要删除的前个数之后的第一个在数组的数的右边是否有个数要删除即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } bool check(vector<bool> &b, int n, int m, int k){ int rm = 0; for(int i = 0; i < n; ++ i){ if (!b[i]) ++ rm; else if (rm >= k / 2 && (n - m - rm) >= k / 2) return true; } return false; } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n, m, k; read(n); read(k); read(m); vector<bool> b(n, false); for(int a, i = 0; i < m; ++i){ read(a); -- a; b[a] = true; } if ((n - m) % (k - 1)){ puts("NO"); continue; } if (check(b, n, m, k)) puts("YES"); else puts("NO"); } return 0; }
I. Plane Tiling (CF contest 1468 I)
题目大意
解题思路
神奇的代码
J. Road Reform (CF contest 1468 J)
题目大意
给定一张无向图,你有一个操作是使某一边的边权加一或减一。要求一个图的生成树,树的所有边的边权的最大值恰好为,求最小的操作数。
解题思路
把边权小于等于边权全部加到图上,如果形成了一棵树,就取图上最大边权和未在图上边权大于的最小边权与的差值的绝对值的最小值。
如果还不能形成一棵树,那就继续加边,把边权弄成,同时累计代价,直到形成一棵树。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } int findfa(int x, vector<int> &fa){ return x == fa[x] ? x : fa[x] = findfa(fa[x], fa); } int main(void) { int kase; read(kase); for (int ii = 1; ii <= kase; ii++) { int n, m; LL k; read(n); read(m); read(k); vector<pair<LL,pair<int,int>>> edge(m); LL w; for(int u, v, i = 0; i < m; ++ i){ read(u); read(v); read(w); -- u; -- v; edge[i] = make_pair(w, make_pair(u, v)); } sort(edge.begin(), edge.end(), [](pair<LL,pair<int,int>> &a, pair<LL,pair<int,int>> &b){ return a.first < b.first; }); vector<int> fa(n); iota(fa.begin(), fa.end(), 0); size_t id = 0; int cnt = 0; for(; id < edge.size(); ++ id){ if (edge[id].first > k) break; auto a = findfa(edge[id].second.first, fa); auto b = findfa(edge[id].second.second, fa); if (a != b){ ++ cnt; fa[a] = b; } } LL ans = 0; if (cnt >= n - 1){ ans = k - edge[id - 1].first; if (id != edge.size()) ans = min(ans, edge[id].first - k); }else{ for(; id < edge.size(); ++ id){ if (cnt == n - 1) break; auto a = findfa(edge[id].second.first, fa); auto b = findfa(edge[id].second.second, fa); if (a != b){ ans += edge[id].first - k; ++ cnt; fa[a] = b; } } } write(ans, '\n'); } return 0; }
K. The Robot (CF contest 1468 K)
题目大意
平面图一个机器人在,给定机器人的移动指令包含,此时机器人最终不能回到原点。现在可以放置一个障碍物(机器人不能移动到障碍物处),使得机器人可以回到,问障碍物的位置,或告知不存在方案。
解题思路
命令数只有,我们就枚举掉的那个命令,即在执行某个命令到达后的位置放置障碍物,看能否回到原点。
值得注意的是如果这个放置障碍物的位置,之前走过,那么这个位置此时不能放置障碍物。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; bool check(string &s, int x, int y, size_t pos){ int bx = x; int by = y; int nx = x, ny = y; if (s[pos] == 'L') -- bx; if (s[pos] == 'R') ++ bx; if (s[pos] == 'U') ++ by; if (s[pos] == 'D') -- by; for(size_t i = pos; i < s.size(); ++ i){ nx = x; ny = y; if (s[i] == 'L') -- nx; if (s[i] == 'R') ++ nx; if (s[i] == 'U') ++ ny; if (s[i] == 'D') -- ny; if (nx != bx || ny != by){ x = nx; y = ny; } } return x == 0 && y == 0; } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int kase; cin>>kase; for (int ii = 1; ii <= kase; ii++) { string s; cin >> s; map<pair<int,int>, bool> qwq; int x = 0, y = 0; bool sign = false; for(size_t i = 0; i < s.size(); ++ i){ if (check(s, x, y, i)){ sign = true; } if (s[i] == 'L') -- x; if (s[i] == 'R') ++ x; if (s[i] == 'U') ++ y; if (s[i] == 'D') -- y; if (sign && qwq[{x, y}]) sign = false; qwq[{x, y}] = true; if (sign) break; } if (!sign) x = 0, y = 0; cout << x << ' ' << y << endl; } return 0; }
L. Prime Divisors Selection (CF contest 1468 L)
题目大意
解题思路
神奇的代码
M. Similar Sets (CF contest 1468 M)
题目大意
给定个集合,第个集合包含个数。求一对相似集合,或告知不存在。
两个集合,如果有两个不同的数都在这两个集合里,则这两个集合相似。
解题思路
神奇的代码
N. Waste Sorting (CF contest 1468 N)
题目大意
给了三种垃圾桶及其容量,以及五种垃圾数量。扔,扔,扔,扔或,扔或。问最后能不能把垃圾扔到正确的垃圾桶里且垃圾桶不爆满。
解题思路
扔;扔;扔;扔,满了扔;扔,满了扔。
看能不能塞下。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; bool check(){ int a1, a2, a3; int v1, v2, v3, v4, v5; cin >> a1 >> a2 >> a3 >> v1 >> v2 >> v3 >> v4 >> v5; a1 -= v1; a2 -= v2; a3 -= v3; if (a1 < 0) return false; if (a2 < 0) return false; if (a3 < 0) return false; int cnt1 = min(a1, v4); a1 -= cnt1; a3 -= v4 - cnt1; int cnt2 = min(a2, v5); a2 -= cnt2; a3 -= v5 - cnt2; if (a1 < 0) return false; if (a2 < 0) return false; if (a3 < 0) return false; return true; } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int kase; cin>>kase; for (int ii = 1; ii <= kase; ii++) { if (check()) cout << "YES" <<endl; else cout << "NO" << endl; } return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/14194810.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现