集训总结(年前)

2024-02-05 23:28
文章标签 总结 集训 年前

本文主要是介绍集训总结(年前),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.29 1.29 1.29 ~ 2.4 2.4 2.4 集训总结

集训学了数据结构优化DP、动态开点线段树、可持久化线段树、图论。

打了四场比赛,每场比赛都有 8 8 8 题,时间 3 3 3 小时 15 15 15 分钟。

题目

1. 1. 1. 踢踏舞

题面

小 C 和小 T 最近在学习踢踏舞。这种舞蹈大部分由用特制舞鞋击打地板的动作组成。小 C 和小 T 学得很快,他们甚至开始自己编排舞曲。

踢踏舞的编舞可以用一串包含两种字母 LR的序列来表示。

L表示用左脚击打地板,R表示用右脚击打地板。小 C 意识到踢踏舞中最精彩的部分就是舞蹈中两只脚互相交替击打地板的部分,即在这部分中,没有连续的两次动作是用同一只脚击打地板的。他定义一种踢踏舞编舞的价值为序列中最长的一段连续子序列,其中不包含连续的两个LR

我们都知道,编舞的工作非常有挑战性,需要做出很多的调整,直到完成编舞。对于每次小 T 的调整,小 C 想要知道目前这种编舞的价值。小T的一次调整即为将序列中的 L改为 R,反之亦然。 在小T开始调整之前,整段序列只由 L组成。

输入格式及范围

第一行包括两个正整数,编舞序列的长度 N N N ( 1 ⩽ N ⩽ 200000 1\leqslant N \leqslant 200 000 1N200000),以及小T调整的次数 Q Q Q( 1 ⩽ Q ⩽ 200000 1 \leqslant Q \leqslant 200 000 1Q200000).

接下来有 Q Q Q 行,每行包括一个数字 x x x 表示小T调整的动作在序列中的位置。

输出格式

输出包括 Q Q Q 行,每行数字表示小T每次调整之后编舞序列的价值。

样例1
输入
6 2
2
4
输出
3 5
样例2
输入
6 5
4
1
1
2
6
输出
3
3
3
5
6
解法

此题很容易想出,正解的复杂度是 Θ ( N log ⁡ N ) \Theta(N \log N) Θ(NlogN) 的。

此题通过题目可知不能离线。

又有 N N N 次修改查询,所以只能用每一次 Θ ( N log ⁡ N ) \Theta(N \log N) Θ(NlogN) 的算法。

我们可以想到分治。

分治可以有三种情况:

  1. 答案区间都在中间点左边

  2. 答案区间都在中间点右边

  3. 答案区间跨越中间点

如下图:

对于前 2 2 2 种情况,可以直接递归下去。

对于第 3 3 3 种情况,我们可以拆成两个区间,如图。

又由于是区间操作,所以可以使用线段树。

线段树可以保存 3 3 3 个值:答案、紧贴左边的、紧贴右边的。

计算的代码如下:

void Update(Node &This, Node LChild, Node RChild) {This.LAns = LChild.LAns;This.RAns = RChild.RAns;This.Ans = std::max(LChild.Ans, RChild.Ans);if (Dance[LChild.R] != Dance[RChild.L]) {This.Ans = std::max(This.Ans, LChild.RAns + RChild.LAns);if (LChild.LAns == LChild.R - LChild.L + 1) {This.LAns += RChild.LAns;}if (RChild.LAns == RChild.R - RChild.L + 1) {This.RAns += LChild.RAns;}}}

其它就是一棵单点修改区间查询的线段树了,以下是代码:

#include <algorithm>
#include <iostream>
#include <string>std::string Dance;
int N, Q;class SegmentTree {
private:struct Node {int L, R, Ans, LAns, RAns;} SegmentTreeArr[800005];void Update(Node &This, Node LChild, Node RChild) {This.LAns = LChild.LAns;This.RAns = RChild.RAns;This.Ans = std::max(LChild.Ans, RChild.Ans);if (Dance[LChild.R] != Dance[RChild.L]) {This.Ans = std::max(This.Ans, LChild.RAns + RChild.LAns);if (LChild.LAns == LChild.R - LChild.L + 1) {This.LAns += RChild.LAns;}if (RChild.LAns == RChild.R - RChild.L + 1) {This.RAns += LChild.RAns;}}}public:void Build(int NodeId = 1, int L = 1, int R = N) {SegmentTreeArr[NodeId] = {L, R, 1, 1, 1};if (L == R) return;int M = (L + R) / 2;Build(NodeId * 2, L, M);Build(NodeId * 2 + 1, M + 1, R);}void Edit(int Position, int NodeId = 1) {if (SegmentTreeArr[NodeId].L == SegmentTreeArr[NodeId].R) {if (Dance[Position] == 'L') Dance[Position] = 'R';else Dance[Position] = 'L';return;}int M = (SegmentTreeArr[NodeId].L + SegmentTreeArr[NodeId].R) / 2;if (Position <= M) {Edit(Position, NodeId * 2);} else {Edit(Position, NodeId * 2 + 1);}Update(SegmentTreeArr[NodeId], SegmentTreeArr[NodeId * 2], SegmentTreeArr[NodeId * 2 + 1]);}int Query(int L, int R, int NodeId = 1) {if (L <= SegmentTreeArr[NodeId].L && SegmentTreeArr[NodeId].R <= R) return SegmentTreeArr[NodeId].Ans;if (L > SegmentTreeArr[NodeId].R || R < SegmentTreeArr[NodeId].L) return 0;int M = (SegmentTreeArr[NodeId].L + SegmentTreeArr[NodeId].R) / 2;return Query(L, M, NodeId * 2) + Query(M + 1, R, NodeId * 2 + 1);}
} C1888_P5;int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);std::cin >> N >> Q;for (int i = 0; i <= N; i++) {Dance += 'L';}C1888_P5.Build();for (int i = 1; i <= Q; i++) {int Pos;std::cin >> Pos;C1888_P5.Edit(Pos);std::cout << C1888_P5.Query(1, N) << '\n';}return 0;
}
理解

线段树不仅仅能求区间和,还可以求一些分治的问题。

2. 2. 2. 求负环、毕加猪

求负环很简单,大家都知道。
代码:

#include <cstring>
#include <iostream>using namespace std;
int Vertex, Edge;
struct EdgeNode {int VertexStart, VertexEnd, Weight;
};
EdgeNode Edges[10005];
int Dis[1005];
#define ERR_NegativeRing (-0x114514)
#define ERR_NoPath (-0x1919810)
#define NoPath 0x3F3F3F3Fint BellmanFord(int Start, int End) {memset(Dis, 0x3F, sizeof(Dis));Dis[Start] = 0;bool Update;for (int i = 1; i <= Vertex + 1; i++) {Update = false;for (int j = 1; j <= Edge; j++) {int CurrentStart, CurrentEnd, CurrentWeight;CurrentStart = Edges[j].VertexStart;CurrentEnd = Edges[j].VertexEnd;CurrentWeight = Edges[j].Weight;if (Dis[CurrentStart] + CurrentWeight < Dis[CurrentEnd]) {Dis[CurrentEnd] = Dis[CurrentStart] + CurrentWeight;Update = true;}}if (!Update)break;i++;}if (Dis[End] == NoPath)return ERR_NoPath;else if (Update)return ERR_NegativeRing;elsereturn Dis[End];
}int main() {cin >> Vertex >> Edge;for (int i = 1; i <= Edge; i++) {cin >> Edges[i].VertexStart >> Edges[i].VertexEnd >> Edges[i].Weight;}int Ans = BellmanFord(1, Vertex);if (Ans == ERR_NegativeRing) {cout << "YES\n";} else {cout << "NO\n";}return 0;
}
正题:毕加猪
题面

圣诞节那天,大家聚在一起开 party 庆祝。猪丽叶和嫦娥在聊天,

当她们聊到毕加猪的时候,她们发现小猪竟然对她们都表白过。

她们很生气,决定一起整整小猪(花心的小猪完蛋咯!!)。

她们都去约小猪,要小猪在明天的 A A A 时刻到她们家,不然就不理小猪。

小猪不想失去她们任何一个,但是他又不能在同一时间出现在两个不同的地方。

小猪突然想起来,猴哥的月光宝盒可以穿越时空,于是他向猴哥借来了月光宝盒,

但是它只能在指定的位置使用。有了月光宝盒,小猪可以先到其中一个MM家,待 T T T 分钟之后,再借助月光宝盒,穿越时空,让他能在 A A A 时刻出现在另一个MM家。

(故事背景无不良引导,不要关注)

输入格式

第一行一个整数 c a s cas cas,表示每一个测试点有 c a s cas cas 组数据。

每组测试数据第一行包含三个整数 N N N M M M,和 T T T,表示 N N N 个点, M M M 条边和小猪需要在MM家待的时间。

接下来行,每行三个整数 a a a b b b c c c 表示有一条边从 a a a b b b 要花 c c c 时间,

如果 c c c 为负表示可以在 a a a 点使用月光宝盒,到达点,回到 ∣ c ∣ |c| c 时间以前。

最后一行两个整数 p p p q q q,表示猪丽叶家和嫦娥家的编号。

输出格式

对于每组测试数据,输出 yes或者 no,表示小猪能不能同时满足两个MM的要求。能的话输出 yes否则输出 no

样例1
输入
1
3 2 1
1 2 3
2 3 5
1 3
输出
no
样例2
输入
2
3 2 1
1 2 3
2 3 -5 
1 3
3 2 1
1 2 3
2 3 -3
1 3
输出
yes
no
数据范围

对于 30 % 30\% 30% 的数据: N ⩽ 20 N \leqslant 20 N20 M ⩽ 100 M \leqslant 100 M100 c a s ⩽ 10 cas \leqslant 10 cas10 ∣ c ∣ ⩽ 1000 |c| \leqslant 1000 c1000

对于 60 % 60\% 60% 的数据: N ⩽ 100 N \leqslant 100 N100 M ⩽ 1000 M \leqslant 1000 M1000 c a s ⩽ 10 cas \leqslant 10 cas10 ∣ c ∣ ⩽ 1000000 |c| \leqslant 1000000 c1000000

对于 90 % 90\% 90% 的数据: N ⩽ 200 N \leqslant 200 N200 M ⩽ 3000 M \leqslant 3000 M3000 c a s ⩽ 10 cas \leqslant 10 cas10 ∣ c ∣ ⩽ 1000000 |c| \leqslant 1000000 c1000000

对于 100 % 100\% 100% 的数据: N ⩽ 1000 N \leqslant 1000 N1000 M ⩽ 10000 M \leqslant 10000 M10000 c a s ⩽ 10 cas \leqslant 10 cas10 ∣ c ∣ ⩽ 1000000 |c| \leqslant 1000000 c1000000

解法

不必使用 SPFA,因为SPFA已死 Θ ( N × M ) \Theta(N\times M) Θ(N×M)足够用了。

此题本质上就是一个求负环,但是求负环的时候要求出到底能不能到达终点,判断是不是“死环”(如下图)。

而且也并不是只能因为负环而输出 Yes,而且可以通过“负路径”也可以输出 Yes

代码
#include <cstring>
#include <iostream>
#include <vector>using namespace std;
int Vertex, Edge;
struct EdgeNode {int VertexStart, VertexEnd, Weight;
};
EdgeNode Edges[10005];
int Dis[1005], Vis[1005];
int Update2;
std::vector<int> Graph[1005];
#define ERR_NegativeRing (-0x114514)
#define ERR_NoPath (-0x1919810)
#define NoPath 0x3F3F3F3Fbool Finish(int Node1, int End) {if (Node1 == End) return true;Vis[Node1] = Update2;for (int Node2: Graph[Node1]) {if (Vis[Node2] != Update2 && Finish(Node2, End)) return true;}return false;
}int Time, Pig1, Pig2;bool BellmanFord(int Start, int End) {memset(Dis, 0x3F, sizeof(Dis));Dis[Start] = Time;bool Update;for (int i = 1; i < Vertex; i++) {Update = false;for (int j = 1; j <= Edge; j++) {int CurrentStart, CurrentEnd, CurrentWeight;CurrentStart = Edges[j].VertexStart;CurrentEnd = Edges[j].VertexEnd;CurrentWeight = Edges[j].Weight;if (Dis[CurrentStart] + CurrentWeight < Dis[CurrentEnd]) {Dis[CurrentEnd] = Dis[CurrentStart] + CurrentWeight;Update = true;}}if (!Update) break;}if (Dis[End] <= 0) return true;for (int j = 1; j <= Edge; j++) {int CurrentStart, CurrentEnd, CurrentWeight;CurrentStart = Edges[j].VertexStart;CurrentEnd = Edges[j].VertexEnd;CurrentWeight = Edges[j].Weight;if (Dis[CurrentStart] + CurrentWeight < Dis[CurrentEnd]) {//Dis[CurrentEnd] = Dis[CurrentStart] + CurrentWeight;Update2++;if (Finish(CurrentStart, End)) return true;}}return false;
}int main() {int T;std::cin >> T;for (int t = 1; t <= T; t++) {cin >> Vertex >> Edge >> Time;for (int i = 1; i <= Vertex; i++) Graph[i].clear();for (int i = 1; i <= Edge; i++) {cin >> Edges[i].VertexStart >> Edges[i].VertexEnd >> Edges[i].Weight;Graph[Edges[i].VertexStart].push_back(Edges[i].VertexEnd);}std::cin >> Pig1 >> Pig2;if (BellmanFord(Pig1, Pig2) || BellmanFord(Pig2, Pig1)) std::cout << "yes\n";else std::cout << "no\n";}return 0;
}

3. 3. 3. 数列 max 更新

题面

DCrusher 有一个数列,初始值均为 0 0 0,他进行 N N N 次操作,每次将数列 [ a , b ) [a, b) [a,b) 这个区间中所有比 k k k 小的数改为 k k k

他想知道 N N N 次操作后数列中所有元素的和。他还要玩其他游戏,所以这个问题留给你解决。

输入格式

第一行一个整数 N N N,然后有 N N N 行,每行三个正整数 a , b , k a,b,k a,b,k

输出格式

一个数,数列中所有元素的和

样例
输入
4
2 5 1
9 10 4
6 8 2
4 6 3
输出
16
数据范围

N ⩽ 400 , a , b , k ⩽ 1 0 9 N \leqslant 400, a,b,k \leqslant 10^9 N400,a,b,k109

解法

本题是主席树较板子题(可以离散化)。
区间修改区间查询。

代码
#include <iostream>typedef long long LL;
LL N;
LL A, B, K;class SegmentTree {
public:LL LChild[5000005], RChild[5000005], Max[5000005], Total, Root1;void Update(LL L, LL R, LL QueryL, LL QueryR, LL Val, LL &Root) {if (!Root) Root = ++Total;if (QueryL <= L && R <= QueryR) {Max[Root] = std::max(Max[Root], Val);return;}LL Mid = (L + R) / 2;if (QueryL <= Mid) Update(L, Mid, QueryL, QueryR, Val, LChild[Root]);if (QueryR > Mid) Update(Mid + 1, R, QueryL, QueryR, Val, RChild[Root]);}LL Query(LL L, LL R, LL Val, LL Root) {Max[Root] = std::max(Max[Root], Val);Val = std::max(Max[Root], Val);if (L == R) return Max[Root];LL M = (L + R) / 2, Ans = 0;if (LChild[Root]) Ans += Query(L, M, Val, LChild[Root]);else Ans += (M - L + 1) * Max[Root];if (RChild[Root]) Ans += Query(M + 1, R, Val, RChild[Root]);else Ans += (R - M) * Max[Root];return Ans;}
} C1894_T7;int main() {std::cin >> N;for (int i = 1; i <= N; i++) {std::cin >> A >> B >> K;C1894_T7.Update(1, 100000000, A, B - 1, K, C1894_T7.Root1);}std::cout << C1894_T7.Query(1, 100000000, 0, C1894_T7.Root1) << '\n';return 0;
}

这篇关于集训总结(年前)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/682497

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

整数Hash散列总结

方法:    step1  :线性探测  step2 散列   当 h(k)位置已经存储有元素的时候,依次探查(h(k)+i) mod S, i=1,2,3…,直到找到空的存储单元为止。其中,S为 数组长度。 HDU 1496   a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 。 x在 [-100,100] 解的个数  const int MaxN = 3000

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

go基础知识归纳总结

无缓冲的 channel 和有缓冲的 channel 的区别? 在 Go 语言中,channel 是用来在 goroutines 之间传递数据的主要机制。它们有两种类型:无缓冲的 channel 和有缓冲的 channel。 无缓冲的 channel 行为:无缓冲的 channel 是一种同步的通信方式,发送和接收必须同时发生。如果一个 goroutine 试图通过无缓冲 channel

9.8javaweb项目总结

1.主界面用户信息显示 登录成功后,将用户信息存储在记录在 localStorage中,然后进入界面之前通过js来渲染主界面 存储用户信息 将用户信息渲染在主界面上,并且头像设置跳转,到个人资料界面 这里数据库中还没有设置相关信息 2.模糊查找 检测输入框是否有变更,有的话调用方法,进行查找 发送检测请求,然后接收的时候设置最多显示四个类似的搜索结果

java面试常见问题之Hibernate总结

1  Hibernate的检索方式 Ø  导航对象图检索(根据已经加载的对象,导航到其他对象。) Ø  OID检索(按照对象的OID来检索对象。) Ø  HQL检索(使用面向对象的HQL查询语言。) Ø  QBC检索(使用QBC(Qurey By Criteria)API来检索对象。 QBC/QBE离线/在线) Ø  本地SQL检索(使用本地数据库的SQL查询语句。) 包括Hibern

暑期学习总结

iOS学习 前言无限轮播图换头像网络请求按钮的configuration属性总结 前言 经过暑期培训,完成了五个项目的仿写,在项目中将零散的内容经过实践学习,有了不少收获,因此来总结一下比较重要的内容。 无限轮播图 这是写项目的第一个难点,在很多项目中都有使用,越写越熟练。 原理为制造两个假页,在首和尾分别制作最后一页和第一页的假页,当移动到假页时,使用取消动画的方式跳到