SDSC2021 游记
前言
我尽量每日更新昨天的(
大约就是 SDSC 的游记罢,大概是时间线游记罢。
现在各种东西的排版可能很奇怪,可能等集训结束之后会整一下。
来自集训后,不想整了就这样罢ww
Day -1
早上起来从 JN 往 RZ 日照走,最一开始 JN 在下雨,可能是在高速上跑的比云快,(?)于是最后到 RZ 的时候就大约不太下雨了,30+ 度确实已经很热了(
然后先去定酒店,然后定完酒店雨也跟着过来了。
于是决定下去找个地方吃饭,但是雨太大于是我们就淋了一身雨(在打着伞的情况下)回了宾馆,收获巨大(?)
在酒店等外卖的时候随便写了一个橙题然后大约饭就来了,吃完饭就准备出去 Van♂
去海岸摸了摸沙子,然后去某个神必小镇吃晚饭。
Day 0
也不知道几点起的床,然后去海上森林公园,在园区骑车骑得很累。
下午去报道,发现宿舍很拉,之前有人给我说下铺是桌子上铺是床,到了发现完全不是这样的(
然后晚上随便看了几道题,但是不是很想做,于是就 F1 2016 退退退(指退赛)
终于是把阿塞拜疆赛道整完了,应该是把 RP 用完了罢
序章
本来是要把所有的莉题也整上的,但是发现时间不太够于是就先在这里把知识点都搞出来。
当然这里也会穿插一些吐槽什么的,是纯正的游记罢
Day 1
早上 5:50 就起了,整了一些奇怪的歌打了打来清醒。
话说宿舍里我手机没有信号就离谱(
七点去吃饭。因为本来是要求的七点起床,所以觉得自己还是去的挺早的(
但是餐厅人已经非常之多了,很恐怖,早饭也很拉,非常想念 LCEZ 的饭。
Day 1 是图论的第一天,讲图论的是孙铭远巨神(
说实话 SDSC 2021 排班挺奇怪的,数据结构提高班先讲图论(
上课的地方和宿舍离得不近,而且路比较绕,废了很大的劲才找到,更可恨的是这个学校的路标只标出了路的名字,但是没有每个楼的名称,导致我很难才找到上课的地方。
因为日照这几天一直在下雨,于是这个报告厅也很潮。
最短路
可能是觉得大家都有基础了就没有做过多的铺垫。
单源最短路
Dijkstra
上来简单说了一下 Dijkstra 的贪心策略,然后强调了一下:关于 SPFA,她死了(。
目前所有提出的 SPFA 的优化都能被卡((
Dijkstra 的堆优化是 \(O(m\log n)\) 的,用了 zkw 线段是就能达到 \(O(m\log n)\),老师说这是最快了(
只能说是zkw巨神除了相亲完全胜利(
但是老师一问发现好多都没学线段树就没讲。
当然还有桶优化,是 \(O(m+w)\) 的,这里 \(w\) 是边权之和。
做法如下:
-
开一个桶(vector)
co
,co[x]
里面pushback
的表示最短路为 \(x\) 的点的编号,也就是说如果dis[u]
被更新为 \(k\),那么co[k].push_back(u);
-
每次仍然选择当前最短路最小的点,直接从桶里面扫描即可。
-
因为一个结点的编号可能会出现多次,而删除是相对很耗费时间的,但是我们注意到在遍历桶的时候,最短路小的是先遍历到的,于是我们只需要考虑每个节点第一次出现即可,后面的出现肯定没有这个优。
当然,因为这个优化的复杂度是和边权大小相关的,于是这个优化在边权小的时候比较实用(
SPFA
当然,这是一个死掉的算法,但是她还是有点用的(指在费用流和判断负环上)
常见的 SPFA 判断负环是用进队元素数是否大于等于 \(n\) 来判断,但是这样太慢了。于是我们可以记录深度,如果深度大于 \(n\) 了就会出现负环,这样快一点。
多源最短路
Floyd 就不说了,枚举一个中间点 \(O(n^3)\)。
不过要注意这里的枚举中间点的循坏应当在最外层,也就是 \(kij\) 否则会退化成 \(O(n^4)\)。
Johnson 大约就是跑 n 遍 Dijkstra 的样子。
次短路
实际上就是同时开一个数组维护一下啊次短路即可,大约核心代码就是这样的,相信都能看懂。
(fdis 是最短路,sdis 是次短路)
I void Dijkstra()
{
mst(fdis,0x7f),mst(sdis,0x7f);
priority_queue< pair<int,int> , vector< pair<int,int> > ,greater< pair<int,int> > > q;
q.push(make_pair(0,1));
while(q.size())
{
int dis=q.top().first,x=q.top().second;q.pop();
if(dis>sdis[x]) continue;
for(R int i(head[x]);i;i=r[i].nex)
{
int y=r[i].to;
if(fdis[y]>dis+r[i].val)
{
sdis[y]=fdis[y];
fdis[y]=dis+r[i].val;
q.push(make_pair(fdis[y],y));
}
if(sdis[y]>dis+r[i].val and dis+r[i].val>fdis[y])
{
sdis[y]=dis+r[i].val;
q.push(make_pair(sdis[y],y));
}
}
}
}
最长路
用 SPFA 跑,三角形不等式处松弛的时候改一下不等号方向即可。
老师说一般没有卡 SPFA 的,但是貌似下午有人就找出来卡 SPFA 的题了(
差分约束系统
简单来说就是,把题目给出的模糊的关系转化为不等式,再联系三角形不等式转化到最短路/最长路问题上。
简单用一句话总结一下就是:
-
求最小值,大于等于,最长路
-
求最大值,小于等于,最短路
要注意的一点是,除了题目明确给出的条件,还要注意隐藏的上下界。
在建图的时候常常会用到虚拟源点,因为根据不等式建出来的图不一定是联通的(
最小生成树
Kruskal 和 Prim 就不说了。
简单介绍一种我之前没见过的算法:boruvka,可能会在稍微难一点的题目中提到。
她同时具有 Prim 和 Kruskal 算法的特点。
Prim 的核心思想是维护一个当前生成树,每次选取一个最小的边连接一个点,而她是维护多个生成树,每轮都从每个生成树向外选择最小的边去连接其他的生成树。
最一开始每个结点都是一个生成树,这和 Kruskal 的并查集思想相近。
最小瓶颈路
貌似就一条:最小瓶颈路 = 最小生成树。
Kruskal 重构树
简单来说就是在合并集合的时候把结点加入一个新的树,然后可以就有很多神必性质,比如说找 LCA 或者是判断联通什么的。
Day 2
Day 2 换了个大一点的报告厅,群里有人开了位置共享,于是找到位置也不难的样子(
由于 Day 2 的博客大约是很久之前写的,于是可能会有一定的莉题(
Day 2 还是图论的内容(数据结构 TG 讲图论hhh)
欧拉路相关
实际上欧拉路很久之前就写过了,定义十分好理解,主要是如何把题目转化为欧拉路相关芝士求解。
定义
-
欧拉路径:从一个点出发,不重不漏的经过图中每一条边的一条路径(允许多次经过同一个点)。
-
若为无向图,需要保证联通且图中恰好存在两个点的度数是奇数,其他节点的度数为偶数,这两个度数为奇数的点就是起点与终点;或者所有点度数都是偶数。
-
若为有向图,则需连通,且图中恰好存在一个点入度比出度多一,一个点出度比入度多一,其他节点的出度等于入度;或者所有点入度等于出度。
-
-
欧拉回路:起点和终点是一个点的欧拉路径。
- 要求图联通。若为无向图,要求所有点的度数为偶数;若为有向图,要求所有点的入度等于出度。
求解
求解使用 \(\tt{Hierholzer}\) 算法。
具体实现的过程依赖于 DFS 遍历。具体实现就不能走重复边,但是可以重复的经过一个点。
但是有一点关于复杂度的问题就是:如果直接用 \(vis\) 数组来判断一条边有没有被遍历过的话,复杂度是过高的,达到了 \(O(\frac{n^2}{2})=O(n^2)\),于是我们额外记录每个点的出边。第二维记录当前到第几条边了,若本次到了第 \(k\) 边了,那么下一次从第 \(k+1\) 次开始。
实际上说白了欧拉回路就是平常我们的一笔画问题罢了。
运用
因为当天下午的练习赛只有莉题二,于是只有莉题二放代码罢。
莉题一
那么肯定是第二种会更好一点,因为第一种我们不能保证每个单词只经过一次,但是第二种我们把单词作为边就能去跑欧拉路(
莉题二
先考虑简单情况,我们把每行每列都建立一个点,然后把每个给定坐标点的行和列连起来,然后把染红看作从行指向列,蓝色反之。于是合法的染色方式就对应到了图上每个点的入度等于出度(根据简化版题意有偶数个点于是每个点在合法情况下的出度和入度都是一样的)
需要注意的是建完图之后可能不会全联通,于是我们对每个连通块求欧拉回路。
于是在回来考虑原题的思路,因为不保证事偶数了,于是我们考虑如何转化到偶数的情况。
然后老师给出的 solution 是建立一个虚拟节点(
于是把所有度数为奇数的点连到虚拟节点上,然后求欧拉回路,最后把虚拟节点及其相关边删掉就可。
Code
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>
#include <vector>
#define Heriko return
#define Deltana 0
#define Romanno 1
#define S signed
#define LL long long
#define R register
#define I inline
#define CI const int
#define mst(a, b) memset(a, b, sizeof(a))
#define ON std::ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
template<typename J>
I void fr(J &x)
{
short f(1);
char c=getchar();
x=0;
while(c<'0' or c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while (c>='0' and c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
x*=f;
}
I void fw(bool k)
{
if(k) putchar('r');
else putchar('b');
}
CI MXX=4e5+5;
int head[MXX],cnt,n;
struct node
{
int nex,to;
}
r[MXX<<1];
I void Add(int x,int y) {r[++cnt].to=y;r[cnt].nex=head[x];}
int color[2][MXX],vis[MXX];
vector<int> e[MXX];
void DFS(int x,int y)
{
if(vis[x]) Heriko;
else vis[x]=y;
for(R int i(0);i<(int)e[x].size();++i) DFS(e[x][i],y^1);
}
int x,y;
I void Into()
{
mst(vis,0);
fr(n);
for(R int i(1);i<=n;++i)
{
fr(x),fr(y);
if(color[0][x])
{
e[i].push_back(color[0][x]);
e[color[0][x]].push_back(i);
color[0][x]=0;
}
else color[0][x]=i;
if(color[1][y])
{
e[i].push_back(color[1][y]);
e[color[1][y]].push_back(i);
color[1][y]=0;
}
else color[1][y]=i;
}
}
S main()
{
Into();
for(R int i(1);i<=n;++i)
{
DFS(i,0);
if(vis[i]) fw(1);
else fw(0);
}
Heriko Deltana;
}
树上问题相关
树上的序
- DFS 序:在每个节点第一次被访问的时候加入序列,Tarjan 算法就是基于 DFS 序实现的,没错就是那个时间戳 \(\tt{dfn}\)。
重链剖分
重链就是连接重儿子的边,而每个根结点的重儿子就是相对来说节点数量更多的子树。
具体实现过程就是,DFS 剖第一遍,然后按照重儿子优先访问的规则再做一次 DFS。
维护一个 top
,代表意义是距离最远的重父亲结点。定义轻儿子的 top
是她自己。
代码就不给了,可以看看课上笔记辅助理解(
-
优 美 性 质 :一条链上最多 \(\log n\) 条轻边,\(\log n\) 条重边。
-
可以用她来求 LCA。
DSU ON Tree
树上启发式合并,这部分建议看我课上笔记(
Test
下午练习赛 500/600,RK7,还行。
Day 3
又回到了那个小报告厅,不过这几天一直没下雨于是天气还不错,里面没那么潮了(
最一开始老师也迷路了,然后过来之后 Windows 还自动更新(虽然我下午开我自己电脑的时候也更新了)
巨硬真是 太 万 恶 了!
因为要讲数论(数据结构提高班又开始讲数论了hhhh)于是老师问了一句:不会线性筛的举手,然后居然举手的一大片((((
然后抽了个孩子问质数的定义,那个孩子很流畅的回答了,于是得到了老师的肯定:《非常棒,学的很扎实.jpg》
因为知识点我都学过了,而我又不想写莉题,于是就光写写我没学过的和需要注意的。
于是记录一些语录:
老师在讲完证明的时候问有多少没听懂的,居然有不少,但是已经讲了好几遍了,于是就有了:《我们 OIer 不讲证明,不会证明也问题不大.jpg》
这个时候我左边坐了两个老师,上课期间在吃东西和......睡觉......
实际上今天很奇怪,明明知识点都是基础玩意,但是题目都是奇奇怪怪的。
整体体验就和 mopemope 一样( —— 来自 Day 3 晚上打完 mopemope 之后
Test
下午 Test 最一开始在 RK5 左右,后来就因为我没卡常等问题掉到了 RK18 的样子,虽然还是 500/600 就是了。
Day 4
今天还是和昨天差不多,整体体验和 Mopemope 无差。
早上在宿舍楼道外放 Mopemope 成功引人注意
昨天有人说 OneNote 挺好用,于是就用了用,确实除了手写都比备忘录好用,目测应该是模块化 Markdown 语法(
但是最好发现,虽然能同步到电脑上,但是电脑无法导出,手机还只能导出 pdf 和纯文本,这点确实 baka
因为知识点都还会,于是就只提一点点:
-
搜索真的有很多神必用法(
-
二分在“最大化最小值和最小化最大值”的时候非常有用(
Test
晚上因为一些倍增题确实毒瘤,于是就拿了 300 跑路来把前几天的博客补一补(
Day 5
数据结构提高班终于开始讲数据结构了ww
还是 lxl 讲课,我直接快乐,同时深感危险。不过今天学的东西都学过,莉题难度也一般,所以今天很舒服(
因为 OneNote 在导出方面太拉,于是转回备忘录。
第一部分要讲树状数组和线段树。
因为查询的是前缀所以在树状数组里面把原来线段树上的右儿子都去掉即可(不是右子树)
所以 lxl 说是大约就是没有右儿子的线段树。然后投影就挂了
莉题主要讲了一个思想,就是对于数值建立树状数组,当然一般需要离散化什么的(
线段树没什么新东西(
中间稍微说了一下如何用 LCA。
剩下的看笔记罢(
Day 6 & 7
因为也没什么新的知识点就光放课上笔记罢。
Tests
Day 6 成功 AK ,然后晚上去听了非正解选讲,最一开始管理人员说太难推荐高级算法班以下的不要听了,但是我还是厚着脸皮留了下来。毕竟我刚 AK Test 于是觉得我比 DS 提高班大部分人水平高
然后原来大佬眼中的非正解就是线段树分裂,NTT,Min_25,树套树,第 N 分块啊(
最后和 @EdisonBa 羊神一起溜回了宿舍。
Day 7 的 Test 因为时间不够了要回家于是只有 300pts 就滚粗了。
尾声
总体来说这次老师讲的还是可以的,上课体验尚可。
但是啊但是,这个 SDSC 稀硝酸太多了,天天半夜不睡敲墙早上四点半起来在走廊闹腾生怕你不知道它们起来了......
再加上山外条件拉,甚至因为稀硝酸不冲厕所把上课的小报告厅附近的厕所都锁了害得我跑老远去教学楼上厕所成功错过 10min 课......
总体只有上课和做题以外还行吗,可以说本次上完课最大的感受就是:
附录:课上笔记收录
Day 1
话说鸿蒙的动画做的确实很不错(
Day 2
Day 3
Day 4
尝试了一下 OneNote,但是发现没有什么能够互传的东西(
于是就上传到洛谷放个链接罢
Day 5
因为 Day 5 搞了多屏协同,所以备忘录里的东西和这上面差不多了,就多了一些图片(