清北学堂2019.8.10 & 清北学堂2019.8.11 & 清北学堂2019.8.12
Day 5 杨思祺(YOUSIKI)
今天的难度逐渐上升,我也没做什么笔记
开始口胡正解
今天的主要内容是最小生成树,树上倍增和树链剖分
最小生成树
Prim
将所有点分为两个集合,已经和点 1 连通的集合 S、未和点
1 连通的集合 T
计算集合 T 中每个点 u 和集合 S 的距离,d_u=min<u,v>∈E,v∈S{w_u,v }
选取集合 T 中距离 S 最近的点 u,选中对应的边,加入集合 S
重复上面的过程,直到所有点都被加入集合 S
朴素写法时间复杂度较劣,可以采用堆优化至 O((N + M) logN)
Prim 是一个基于贪心的算法,可以采用归纳法和反证法证明其正确性。
首先证明第一条选中的边 e1 一定包含在某最优解方案中
如果最优解中不包含边 e1,则加入 e1 一定会出现环,且环上存在比 e1 大的边,用 e1 替换之答案更优。
假设最优解包含前 k 个选中的边,e1, e2, . . . , ek,则类似地可证明 ek+1 存在于最优解中。
运用归纳法,Prim 算法得到的 n − 1 条边构成最优解
Kruskal
将所有边按照边权从小到大排序# 依次考虑每一条边 < ui, vi >,如果这条边和之前选中的边形成环,则不选中此边;反之,选中此边
当考虑遍所有边后,选中的边一定构成了一棵最小生成树需要并查集的支持,时间复杂度一般认为是 O(M logM)
证明依赖于拟阵的知识。
拟阵
遗传性:若 S 是一个独立集,那么 S 的子集 S′ 是独立集。
遗传性的推论:空集是独立集。
交换性:若 A 和 B 是 S 的两个独立集且 |A| < |B|,那么存在一个元素 x 满足 x < A 且 x ∈ B,使得 A ∪ {x} 是一个独立集
交换性的推论:一个集合的所有极大独立集大小都相同。例:线性无关组、无向图生成森林。
拟阵最优化
拟阵最优化问题:将集合中每个元素赋予一个权值,求权值和最小 (大) 的极大独立集。
拟阵最优化的贪心算法:
维护当前独立集 G,初始为空。将元素按照权值排序,从小到大枚举元素 x,若 G ∪ {x} 是一个独立集,那么就将 x 加入独立集并将 x 的权值累加入答案。最后的结果就是权值和最小的及大独立集。
证明:如果最优解不包含最小元素 x_1,记该集合为 A,创建新的集合B {x_1},利用交换性不断将 A 中元素加入 B 直到 |A| |B|,则有 B 集合为更优解,矛盾。故 x 一定属于最优解。利用数学归纳法,假设已经证明 x_1, x_2, . . . , x_k−1 属于最优解,如果存在最优解不包含 x_k,还是创建新的集合 B {x_1, x_2, . . . , x_k−1, x_k },利用交换性将元素加入 B 得到更优解,矛盾。
例题比较多,就不放了
树上倍增
序列倍增
回忆普通的序列倍增思想,以 ST 表为例。
Fi,j 记录区间 [i, i + 2 j − 1] 内信息(区间和或区间最值)
Fi,j =merge(Fi,,j−1, Fi+2 j−1,j−1 )
取出区间 [l, r] 答案时,使用若干个 Fi,j 即可
如果求区间最值,那么取 Fl,k 和 Fr−2 k ,k 即可,其中 k ⌊log2(r − l + 1⌋
如果求区间和,可以取 Fl,k1 , Fl+2 k1 ,k2 , Fl+2 k1+2 k2 ,k3 , . . . 即可
树上倍增的主要思想
树上从每个点出发到根结点的链也具有类似的形式
Fi,j 表示点 i 向上走 2 j 步的结点
Fi,0 就是点 i 的父亲节点
Fi,j =FFi,j−1,j−1
如何求解 u 向上移动 k 步是哪个点?
将 k 写作 2 的幂次之和,如 11=2 3 + 2 1 + 2 0。 用 Gi,j 表示 i 向上移动 j 步的结果.
Gu,11=FGu,10,0
Gu,10=FGu,8,1
Gu,8=Fu,3 在 O(logN) 步内完成。
LCA(最近公共祖先)
树上倍增最常见的用处是求解两个点的最近公共祖先。
求解 a 和 b 的最近公共祖先
将 a 和 b 调整到相同高度
判断 a 和 b 是否重合,若重合则该点即为答案
令 a 和 b 一起向上移动尽可能大的距离,保证移动后两点 不重合
此时两点的父亲结点即为答案 单次询问时间复杂度 O(logN)
最近公共祖先的常见求解方法有
1.树上倍增
2.树链剖分
3.DFS 序 +RMQ
向上路径
如何求解从 u 到 v 路径上边权最值?保证 v 是 u 的祖先。
Mi,j 表示点 i 出发向上移动 2 j 步经过边权最值
Mi,0=Wi, fatheri
Mi,j =merge(Mi,j−1, MFi,j−1,j−1) 在树上倍增向上走时取移动区间最值更新答案即可。
树上路径
记 g=LCA(u, v),则树上从 u 到 v 的路径可以拆分为两段:
从 u 到 g 的路径
从 g 到 v 的路径
如何求解从 u 到 v 路径上的边权和?
将路径拆分为两段向上路径,分别求解其答案再合并。
树链剖分
首先是一些基本的概念
树链:不拐弯的路径
剖分:每个点属于一条链
重儿子:子树大小最大的子结点
重链:从一点出发,一直选择重儿子向下走,走到叶子
轻边:不属于任何一条重链的边
分析:从任意一点 u 走到根结点,经过的重链、轻边个数的量级 是多少?
走一条重链、轻边,子树大小至少翻倍,故易知 O(logN)。
一些记号
dep_u 表示点 u 的深度
fat_u 表示点 u 的父亲节点
top_u 表示点 u 所在重链的顶端结点
dfn_u 表示重儿子优先 DFS 下点 u 的时间戳
end_u 表示重儿子优先 DFS 下点 u 子树最大时间戳
求点 a 和点 b 的最近公共祖先.
记 ta=top_a , tb=top_b
如果 ta=tb,则 a 和 b 在同一条重链,深度较小的为 LCA
如果 dep_ta > dep_tb,那么令 a=fat_ta
如果 dep_ta < dep_tb,那么令 b=fat_tb
将树序列化
树的结构较为复杂,相较而言我们更喜欢序列这样的一维结构, 因为有丰富的数据结构及其它算法可以处理序列上的种种问题。
那么能否将树转化为序列以便维护信息?
如果按照 dfn_u 将树转化为序列,有哪些有用的性质?
子树是序列中连续一段
树上路径由 O(logN) 个区间组成
下放板子题
luogu P4114 Qtree1
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
long long read()
{
long long a=,b=;
char c=getchar();
while(!isdigit(c))
{
if(c=='-')
b=-;
c=getchar();
}
while(isdigit(c))
{
a=(a<<)+(a<<)+(c^);
c=getchar();
}
return a*b;
}
void out(long long a)
{
if(a>)
out(a/);
putchar(a%+'');
}
const int maxn=;
int n,a[maxn];//a数组表示点与父亲连边的长度
int num,head[maxn];
struct node
{
int to,nxt,dis;
}edge[maxn<<];
void add(int u,int v,int d)
{
edge[num].dis=d;
edge[num].to=v;
edge[num].nxt=head[u];
head[u]=num;
num++;
}
int f[maxn],sz[maxn],dep[maxn],son[maxn];
void dfs(int u,int fa)
{
int maxm=;
sz[u]=;
for(int i=head[u];~i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa)
continue;
f[v]=u;
dep[v]=dep[u]+;
a[v]=edge[i].dis;
dfs(v,u);
sz[u]+=sz[v];
if(sz[v]>maxm)
maxm=sz[v],son[u]=v;
}
}
int b[maxn],in[maxn],top[maxn];//b数组表示当前编号所指的点,in数组表示点的编号,top数组表示点所在链的顶端
void dfs(int u,int fa,int topf)
{
in[u]=++num;
b[num]=u;
top[u]=topf;
if(!son[u])
return;
dfs(son[u],u,topf);
for(int i=head[u];~i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa||v==son[u])
continue;
dfs(v,u,v);
}
}
#define lc p<<1
#define rc p<<1|1
struct Node
{
int val;
}t[maxn<<];
void pushup(int p)
{
t[p].val=max(t[lc].val,t[rc].val);
}
void build(int p,int l,int r)
{
if(l==r)
{
t[p].val=a[b[l]];
return;
}
int m=l+r>>;
build(lc,l,m);
build(rc,m+,r);
pushup(p);
}
void update(int p,int l,int r,int L,int z){
if(l==r)
{
t[p].val=z;
return;
}
int m=l+r>>;
if(m>=L)
update(lc,l,m,L,z);
else
update(rc,m+,r,L,z);
pushup(p);
}
int query(int p,int l,int r,int L,int R)
{
if(l>R||r<L)
return ;
if(L<=l&&r<=R)
return t[p].val;
int m=l+r>>;
return max(query(lc,l,m,L,R),query(rc,m+,r,L,R));
}
void solve_1()//从第0条边开始存,每条边存两次,所以输入的第i条边对应的是第2*i-2和第2*i-1条边,谁是儿子改谁
{
int x=read()*-,d=read(),u=edge[x].to,v=edge[x+].to;
if(f[v]==u)
update(,,n,in[v],d);
else
update(,,n,in[u],d);
}
void solve_2()//链上查询
{
int x=read(),y=read(),ans=;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
swap(x,y);
ans=max(ans,query(,,n,in[top[x]],in[x]));//由于存的是与父亲的距离,而top的父亲直接取所以此处无需加一
x=f[top[x]];
}
if(dep[x]>dep[y])
swap(x,y);
ans=max(ans,query(,,n,in[x]+,in[y]));//但是最后一次存储必须加1否则会多询问一条边
out(ans);
puts("");
}
int main()
{
memset(head,-,sizeof(head));
n=read();
for(int i=;i<n;i++)
{
int x=read(),y=read(),d=read();
add(x,y,d);
add(y,x,d);
}
num=;
dfs(,-);
dfs(,-,);
build(,,n);
char z[];
while()
{
scanf("%s",z);
if(z[]=='D')
break;
switch(z[])
{
case 'C':solve_1();break;
case 'Q':solve_2();break;
}
}
return ;
}
Day 6 杨思祺(YOUSIKI)
这一天主要讲了有关联通分量的内容,下午考试...(真~爆零)
强连通分量
在有向图中,如果两个点之间存在两个方向的路径,则称两个点 强连通;如果有向图的任何两个顶点都强连通,则称其为强连通图;有向图的极大强连通子图即为强连通分量。
缩点
强连通分量最常见的用途是将能互相到达的点集缩为一个新的点,建立的新图一定是有向无环图。
Tarjan 算法
Tarjan 算法可以在 O(N + M) 的时间复杂度内求解有向图的所 有强连通分量。 首先提取出有向图的一棵搜索树作为基础框架。 # d f nu 为 u 点的 DFS 时间戳 # lowu 为 u 点出发能追溯到的最小时间戳 low_u=min{dfn_u , low_v1 , dfn_v2 } 其中 < u, v1 > 为树枝边,< u, v2 > 为后向边
模板
void tarjan(int x) {
dfn[x]=low[x]=++tim;
vis[x]=;
s[++top]=x;
for(int i=head[x];i;i=nxt[i]) {
int y=to[i];
if(!dfn[y])
tarjan(y),low[x]=min(low[x],low[y]);
else if(vis[y])
low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]) {
int y;num++;
do {
y=s[top--];
vis[y]=;
color[y]=num;
size[num]++;
}while(x!=y);
}
}
双连通分量
在无向图中,如果无论删去哪条边都不能使得 u 和 v 不联通, 则称 u 和 v 边双连通;
在无向图中,如果无论删去哪个点(非 u 和 v)都不能使得 u 和 v 不联通,则称 u 和 v 点双连通。
割点:删去该点,图分裂为多个连通块。
割边:也叫“桥”,删去该边,图分裂为多个连通块。
点双连通分量
类似地,定义 dfn_u 和 low_u。 如果 v 是 u 的子结点,并且 low_v ≥ dfn_u 则点 u 是割点,删去点u后 v 子树和其它点不连通
每个割点属于多个点双连通分量,非割点只属于一个点双连通分量。
边双连通分量
类似地,定义 dfn_u 和 low_u。 如果 v 是 u 的子结点,并且 low_v > dfn_u 则边 < u, v > 是割边。
每个点属于一个边双连通分量,边双连通分量之间以割边连接。
Day 7 杨思祺(YOUSIKI)
最后一天,讲了有关二分图和差分约束的知识
二分图
二分图:点黑白染色,邻点不同色。
如何判断一个给定的无向图是不是二分图?
从任意一点开始 BFS 或 DFS,起点不妨染色为白
当前在 u 点时,尝试将所有邻点染为不同的颜色
如果邻点已经染色且颜色不符,则不是二分图
二分图的等价条件
无向图是二分图当且仅当其不包含奇环。
二分图匹配
匹配:选取一些边,使得任意两条边没有公共点(每个点至多属于一条边)
最大匹配:选取边数尽可能多
完美匹配:所有点都在匹配中(每个点恰好属于一条边)
匹配边:选中的边
非匹配边:没有选中的边
匹配点:和选中边相连的点
非匹配点:和选中边不相连的点
常常将二分图的点画成两列
二分图最大匹配是一个常见问题.
匈牙利算法
网络流
匈牙利算法
理论基础
交错路:从非匹配点出发,依次经过非匹配边、匹配边、非匹配边...
增广路:从非匹配点出发,结束于非匹配点的交错路
增广路定理:任意一个非最大匹配的匹配一定存在增广路
算法思想
初始没有选中的边
寻找增广路
找到增广路则将路径中匹配边和非匹配边对换
找不到增广路则当前为最大匹配
网络流
取额外的两个点作为源点和汇点
源点向左边一列每个点连流量为 1 的边
右边一列每个点向汇点连流量为 1 的边
二分图中每条边从左向右连流量为 1 的边
求最大流即可
最小顶点覆盖 Knoig 定理
二分图最小顶点覆盖数等于其最大匹配数。
最小路径覆盖
给定有向图 G < V, E >。
设 P 是 G 的一个简单路(顶点不相 交)的集合。
如果 V 中每个顶点恰好在 P 的一条路上,则称 P 是 G 的一个路径覆盖。
P 中路径可以从 V 的任何一个顶点开始, 长度也是任意的,特别地,可以为 0.
G 的最小路径覆盖是 G 的 所含路径条数最少的路径覆盖。
最小路径覆盖 = |V| - 二分图最大匹配
二分图:将原图每个点拆分为入点和出点,如果原图存在 u 到 v 的边,则在 u 的出点和 v 的入点间连无向边。
差分约束
差分约束可以确定特定的不等式组是否存在解.
xi1 − xj1 ≤ a1
xi2 − xj2 ≤ a2
......
为每个变量 xi 建立一个点 pi.
如果要求 xi − xj ≤ a,则建立一条从 pj 到 pi 长度为 a 的边
以任意一点为起点求单源最短路,则一定有 di − dj ≤ a 如果出现负环,则归结出形如 xi − xi < 0 的约束,不等式组无解。
如果没有负环,最短路算法得到的距离数组就是一组合法的解。
清北学堂2019.8.10 & 清北学堂2019.8.11 & 清北学堂2019.8.12的更多相关文章
- Java学习之JDBC 2019/3/10
Java学习之JDBC 大部分的程序都是用来通过处理数据来达到人们预期的效果,数据是粮食,没有数据操作的程序就像helloworld程序一样没有用处.因此数据库操作是重中之重,是程序发挥功能的基石,j ...
- SPSS 2019年10月24日 今日学习总结
2019年10月24日今日课上内容1.SPSS掌握基于键值的一对多合并2.掌握重构数据3.掌握汇总功能 内容: 1.基于键值的一对多合并 合并文件 添加变量 合并方法:基于键值的一对多合并 变量 2. ...
- 终端、mac等小技巧——2019年10月18日
1.新建finder窗口 cmd+N 2.查看文件夹结构 brew install tree tree命令行参数(只实用与安装了tree命令行工具): -a 显示所有文件和目录. -A 使用ASNI绘 ...
- mac文本操作小技巧——2019年10月17日
声明:看的别人博主写的,自己整理的,非原创,只是自用. mac文本操作技巧 官方指导文档:https://support.apple.com/zh-cn/HT201236 1.光标移动 1.1 行首. ...
- Linux自用指令——2019年10月23日
1.ls ls命令是列出目录内容(List Directory Contents)的意思.运行它就是列出文件夹里的内容,可能是文件也可能是文件夹. ls -a 列出目录所有文件,包含以.开始的隐藏文件 ...
- Gitbook环境搭建及制作——2019年10月24日
1.gitbook介绍 GitBook 是一个基于 Node.js 的命令行工具,支持 Markdown 和 AsciiDoc 两种语法格式,可以输出 HTML.PDF.eBook 等格式的电子书.可 ...
- Visual Studio 2019 v16.10 和 v16.11 Preview 1 现已推出!
Visual Studio 2019 v16.10有什么新功能? 我们很高兴地宣布Visual Studio 2019 v16.10 GA 和 v16.11 preview 1发布.此版本使我们的主题 ...
- 开机时自动启动的AutoHotkey脚本 2019年10月09日
;;; 开机时自动启动的AutoHotkey脚本 2019年10月09日;; http://www.autoahk.com/archives/16600; https://www.cnblogs.co ...
- ;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk
;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk ;~ 此脚本用于测试执行一行或多行AHK脚本源代码的效果;~ 此脚本最后修改于2019年9月22日20时03分;~ 把此 ...
- 等Excel工作簿关闭后自动加密压缩备份2019年10月9日.ahk
;; 等Excel工作簿关闭后自动加密压缩备份2019年10月9日.ahk;; 腾讯QQ号 595076941; 作者:徐晓亮(weiyunwps618); 写作日期:2019年5月15日; 版本号: ...
随机推荐
- 查询SAP数据库表的大小
事物代码DB02 Perfomrmance->Additional Functions->SQL Command Editor->写数据表->执行 select bytes/1 ...
- iOS响应链和传递机制
iOS中加载的时候会先执行main函数 int main(int argc, charchar * argv[]) { @autoreleasepool { return UIApplicationM ...
- Eclipse集成spring-tool-suite(STS)
1.官方下载 sts是spring官方在eclipse基础上加了很多插件之后封装的开发工具.sts与eclipse完全一样,但是多了很多插件,比如maven,使用起来更加方便.如果使用eclipse自 ...
- 使用redislive监控redis
redis监控工具redislive的安装 1. pip安装 如果主机没有pip先安装pip工具 wget --no-check-certificate https://github.com/pypa ...
- SpringMVC基础03——常用注解之@RequestMapping
1.用法 SpringMVC使用@RequestMapping注解,为控制器指定可以处理哪些URL请求,并且可以指定处理请求的类型(POST/GET),如果@RequestMapping没有指定请求的 ...
- shell脚本中的日期处理
Ps:这篇文章只是为了做个分类,以后有看到比较好的时间处理命令都会列在这里,您如果有什么好的时间处理命令,可以评论中添加,我会定期查看更新,谢谢! 1.定义一个参数DATE_TODAY,用于记录当天时 ...
- table 边框问题
对table设置css样式边框,分为几种情况:1.只对table设置边框2.对td设置边框3.对table和td技巧性设置表格边框4.对table和td设置背景,实现完美表格边框 以下DIVCSS5对 ...
- js判断网页标题包含某字符串则替换
js判断网页标题包含某字符串则替换,代码如下: var tit=document.title; if(tit.indexOf("afish")>0){ tit=tit.rep ...
- 关于nmap扫描端口
nmap查看一个服务器的端口,是通过扫描来实现的.所以在本机执行nmap扫描的端口有可能被防火墙阻止,在外部是访问不了的. 如:开启ORACLE监听后,在本机使用nmap 127.0.0.1是可以扫描 ...
- Gym - 101987G Secret Code (概率+数学积分)
题意:有A,B,C三个人要见面,每个人在[0,S]随机选择一个时间点作为见面时间,先到的那个人要等下一个人来了之后和他确认信息,然后马上就走. 例如,假如A先到,B其次,C最后到,那么A要等B到了之后 ...