2020-12-26 22:48阅读: 705评论: 0推荐: 1

2020-2021 ICPC, NERC, Southern and Volga Russian Regional Contest

A. LaIS (CF contest 1468 A)

题目大意

给定数组ai,求最长几乎上升子数组长度。

一个有k个数的数组bi如果满足

min(b1,b2)min(b2,b3)min(b3,b4)...min(bk1,bk)

则数组b成为几乎上升数组。

解题思路

在最长上升子序列的基础商,最长上升子序列允许中间忽然插入一个很大的数。

于是,对于第i个数ai,它可以从前面小于aiaj直接转移过来,或者在(j,i)中插一个数较大的数ak

dp1[i]表示以i结尾的最长几乎上升子序列的最大长度,dp2[i]表示以第i个数结尾的最长几乎上升子序列的最大长度。

从左到右遍历,对于第i个数ai

  • dp1[ai]=maxxai(dp1[x])+1
  • dp1[ai]=maxxai&rposx<i(dp1[x])+2

其中ri表示说位置i右边第一个比它大的数的位置,这个可以事先用单调栈预处理。

posx表示这个数x的位置。

第二种转移即为二维偏序,由于i是递增的,依照rposx更新数据即可,值得注意的是添加的时候,是要添加那个位置的最大长度值,由于dp1已经把这个位置信息丢失了,我们需要dp2来更新。

最值用线段树维护。

神奇的代码
#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)

题目大意

一个餐厅,两个人MP。依次有顾客进来,P知晓顾客消费金额。每次服务,M找最先进来的顾客服务,P找消费金额最高的,相同则最先进来的顾客服务。

现在依次给定发生事件序列,包括

  • 顾客进来,附带P估计的消费金额
  • M去服务顾客
  • P去服务顾客

对于第二和第三种事件,输出它们服务的顾客编号。

解题思路

顾客进来的顺序就是顾客的编号。

用一个数组标记顾客是否被服务,用优先队列维护消费金额。

对于第二种就依次找没被标记的,对于第三种就从优先队列弹出队首直到是未被服务的顾客。

神奇的代码
#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有m个鞭炮,第i个鞭炮点燃后经过ti时刻爆炸,A每个时刻可向左或向右移动一个,或者留在原地点燃一个鞭炮。问A被抓前最多能看到多少个鞭炮爆炸。

解题思路

我们假设A在左,B在右。

首先A不可能往右。

最优方案下,A在原地不断点燃鞭炮,直到BA右边一个格,此时两人再双双向左走,等待鞭炮爆炸。

模拟下这个过程就能得到答案了。

神奇的代码
#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)

题目大意

n个人在平面上的某点朝向某处,现在所有人以相同速度原地顺时针转,直到转了一圈。问期间有多少对人发生了对视。

解题思路

由于角速度相同,容易发现,会发生对视的那两个人它们的朝向在一条直线上,方向相反,用map统计平行且反向的向量对数即可。

神奇的代码
#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)

题目大意

给定1...nn个数,每次操作,选取k个数(k是奇数),删除除中位数之外的数。问最终能否得到给定的有m个数的数组b

解题思路

首先如果nm不是k1的倍数,肯定不行。

思考删除过程,除了最后一次,期间的中位数是任意的,只要最后一次删除,中位数在数组b里即可。

于是我们只要判断要删除的前k2个数之后的第一个在数组b的数的右边是否有k2个数要删除即可。

神奇的代码
#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)

题目大意

给定一张无向图,你有一个操作是使某一边的边权加一或减一。要求一个图的生成树,树的所有边的边权的最大值恰好为k,求最小的操作数。

解题思路

把边权小于等于k边权全部加到图上,如果形成了一棵树,就取图上最大边权和未在图上边权大于k的最小边权与k的差值的绝对值的最小值。

如果还不能形成一棵树,那就继续加边,把边权弄成k,同时累计代价,直到形成一棵树。

神奇的代码
#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)

题目大意

平面图一个机器人在(0,0),给定机器人的移动指令包含LRUD,此时机器人最终不能回到原点。现在可以放置一个障碍物(机器人不能移动到障碍物处),使得机器人可以回到(0,0),问障碍物的位置,或告知不存在方案。

解题思路

命令数只有5000,我们就枚举ban掉的那个命令,即在执行某个命令到达后的位置放置障碍物,看能否回到原点。

值得注意的是如果这个放置障碍物的位置,之前走过,那么这个位置此时不能放置障碍物。

神奇的代码
#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个集合,第i个集合包含ki个数。求一对相似集合,或告知不存在。

两个集合,如果有两个不同的数都在这两个集合里,则这两个集合相似。

解题思路

神奇的代码


N. Waste Sorting (CF contest 1468 N)

题目大意

给了三种垃圾桶A,B,C及其容量,以及五种垃圾a,b,c,d,e,f数量。aA,bB,cC,dACeBC。问最后能不能把垃圾扔到正确的垃圾桶里且垃圾桶不爆满。

解题思路

aAbBcCdA,满了扔CeB,满了扔C

看能不能塞下。

神奇的代码
#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 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(705)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.