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的更多相关文章

  1. Java学习之JDBC 2019/3/10

    Java学习之JDBC 大部分的程序都是用来通过处理数据来达到人们预期的效果,数据是粮食,没有数据操作的程序就像helloworld程序一样没有用处.因此数据库操作是重中之重,是程序发挥功能的基石,j ...

  2. SPSS 2019年10月24日 今日学习总结

    2019年10月24日今日课上内容1.SPSS掌握基于键值的一对多合并2.掌握重构数据3.掌握汇总功能 内容: 1.基于键值的一对多合并 合并文件 添加变量 合并方法:基于键值的一对多合并 变量 2. ...

  3. 终端、mac等小技巧——2019年10月18日

    1.新建finder窗口 cmd+N 2.查看文件夹结构 brew install tree tree命令行参数(只实用与安装了tree命令行工具): -a 显示所有文件和目录. -A 使用ASNI绘 ...

  4. mac文本操作小技巧——2019年10月17日

    声明:看的别人博主写的,自己整理的,非原创,只是自用. mac文本操作技巧 官方指导文档:https://support.apple.com/zh-cn/HT201236 1.光标移动 1.1 行首. ...

  5. Linux自用指令——2019年10月23日

    1.ls ls命令是列出目录内容(List Directory Contents)的意思.运行它就是列出文件夹里的内容,可能是文件也可能是文件夹. ls -a 列出目录所有文件,包含以.开始的隐藏文件 ...

  6. Gitbook环境搭建及制作——2019年10月24日

    1.gitbook介绍 GitBook 是一个基于 Node.js 的命令行工具,支持 Markdown 和 AsciiDoc 两种语法格式,可以输出 HTML.PDF.eBook 等格式的电子书.可 ...

  7. Visual Studio 2019 v16.10 和 v16.11 Preview 1 现已推出!

    Visual Studio 2019 v16.10有什么新功能? 我们很高兴地宣布Visual Studio 2019 v16.10 GA 和 v16.11 preview 1发布.此版本使我们的主题 ...

  8. 开机时自动启动的AutoHotkey脚本 2019年10月09日

    ;;; 开机时自动启动的AutoHotkey脚本 2019年10月09日;; http://www.autoahk.com/archives/16600; https://www.cnblogs.co ...

  9. ;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk

    ;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk ;~ 此脚本用于测试执行一行或多行AHK脚本源代码的效果;~ 此脚本最后修改于2019年9月22日20时03分;~ 把此 ...

  10. 等Excel工作簿关闭后自动加密压缩备份2019年10月9日.ahk

    ;; 等Excel工作簿关闭后自动加密压缩备份2019年10月9日.ahk;; 腾讯QQ号 595076941; 作者:徐晓亮(weiyunwps618); 写作日期:2019年5月15日; 版本号: ...

随机推荐

  1. 查询SAP数据库表的大小

    事物代码DB02 Perfomrmance->Additional Functions->SQL Command Editor->写数据表->执行 select bytes/1 ...

  2. iOS响应链和传递机制

    iOS中加载的时候会先执行main函数 int main(int argc, charchar * argv[]) { @autoreleasepool { return UIApplicationM ...

  3. Eclipse集成spring-tool-suite(STS)

    1.官方下载 sts是spring官方在eclipse基础上加了很多插件之后封装的开发工具.sts与eclipse完全一样,但是多了很多插件,比如maven,使用起来更加方便.如果使用eclipse自 ...

  4. 使用redislive监控redis

    redis监控工具redislive的安装 1. pip安装 如果主机没有pip先安装pip工具 wget --no-check-certificate https://github.com/pypa ...

  5. SpringMVC基础03——常用注解之@RequestMapping

    1.用法 SpringMVC使用@RequestMapping注解,为控制器指定可以处理哪些URL请求,并且可以指定处理请求的类型(POST/GET),如果@RequestMapping没有指定请求的 ...

  6. shell脚本中的日期处理

    Ps:这篇文章只是为了做个分类,以后有看到比较好的时间处理命令都会列在这里,您如果有什么好的时间处理命令,可以评论中添加,我会定期查看更新,谢谢! 1.定义一个参数DATE_TODAY,用于记录当天时 ...

  7. table 边框问题

    对table设置css样式边框,分为几种情况:1.只对table设置边框2.对td设置边框3.对table和td技巧性设置表格边框4.对table和td设置背景,实现完美表格边框 以下DIVCSS5对 ...

  8. js判断网页标题包含某字符串则替换

    js判断网页标题包含某字符串则替换,代码如下: var tit=document.title; if(tit.indexOf("afish")>0){ tit=tit.rep ...

  9. 关于nmap扫描端口

    nmap查看一个服务器的端口,是通过扫描来实现的.所以在本机执行nmap扫描的端口有可能被防火墙阻止,在外部是访问不了的. 如:开启ORACLE监听后,在本机使用nmap 127.0.0.1是可以扫描 ...

  10. Gym - 101987G Secret Code (概率+数学积分)

    题意:有A,B,C三个人要见面,每个人在[0,S]随机选择一个时间点作为见面时间,先到的那个人要等下一个人来了之后和他确认信息,然后马上就走. 例如,假如A先到,B其次,C最后到,那么A要等B到了之后 ...