NOIP冲刺!!!!!
经过我的专断独行长久的讨论,我们仨决定在noip即将到来的7天内每天一个大主题,每人一个分块进行学习复习总结
不管怎么样都要继续努力!!
11.27 周五
今天早放学所以没有训练时间了...但勤劳的我自然还是要复习的!
今天自然不准备写什么了,主要是自己的复习,正式复习从明天开始,这两天都是图论哦
图论——连通性问题
首先对于图论中的基本概念以及求割点是说过的(但不一定记得)所以基础概念直接写一下当做回忆
强连通:在一个有向图G里,设两个点 a b 发现,由a有一条路可以走到b,由b又有一条路可以走到a,我们就叫这两个顶点(a,b)强连通。
连通图:在一个无向图 G 中,若从顶点i到顶点j有路径相连,则称i和j是连通的。如果图中任意两点都是连通的,那么图被称作连通图。如果此图是有向图,则称为强连通图。
割点:对于一个点x∈无向连通图G,若从图中删去改点以及与该点相连的所有边之后,G分裂为两个或两个以上不相连的子图,则称x为G的割点。
割点
稍微回顾一下tarjan算法:
时间戳:在BFS过程中,按照每个节点被第一次访问的顺序,依次给予 N个节点1~N个整数标记。记为dfn[x]。
追溯值:表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值。记为low[x]。
如果x不是根节点,x是割点当且仅当搜索树上存在x的一个子节点y,满足dfn[x]<=low[y]。
若x为根节点,x是割点当且仅当x存在2棵以上子树。
原理很简单:若子节点y的low值>=父节点x的dfn值,说明y只能通过x这一个点与其他点相连,则删去x点后,原图G将分裂为两个不相连的子树。
球法:
假设当前顶点为u,则默认low[u]=dfn[u],即最早只能回溯到自身。
有一条边(u, v),如果v未访问过,继续DFS,DFS完之后,low[u]=min(low[u], low[v]);
如果v访问过(且u不是v的父亲),就不需要继续DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])。
例题:洛谷P3388 割点
模板代码:
1 #include<cmath> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 const int N=2e5+10,M=2e5+10; 9 struct mac 10 { 11 int to; 12 int next; 13 }edge[M]; 14 int head[N],ednum=0;//链式前向星 15 void add(int u,int v) 16 { 17 ednum++; 18 edge[ednum].next=head[u]; 19 edge[ednum].to=v; 20 head[u]=ednum; 21 } 22 int num[N],low[N];//各点时间戳、最小时间戳 23 bool ans[N];//答案 24 int indx,cnt=0;//时间戳、答案个数 25 void tarjan(int u,int fa) 26 { 27 int child=0,v; 28 indx++; 29 num[u]=low[u]=indx; 30 for(int i=head[u];i!=0;i=edge[i].next) 31 { 32 v=edge[i].to; 33 if(!num[v]) 34 { 35 tarjan(v,fa); 36 low[u]=min(low[u],low[v]); 37 if(low[v]>=num[u]&&u!=fa&&!ans[u])//子节点最小时间戳大于父节点且父节点并非根节点 38 { 39 ans[u]=1; 40 cnt++;// 保证u未被标记过之后累计答案数量 41 } 42 if(u==fa) 43 child++; 44 } 45 low[u]=min(low[u],num[v]);//此处不能用else 46 } 47 if(u==fa&&child>=2&&!ans[u]) 48 { 49 ans[u]=1; 50 cnt++; 51 } 52 } 53 int main() 54 { 55 //freopen(".in","r",stdin); 56 //freopen(".out","w",stdout); 57 int n,m,u,v; 58 scanf("%d%d",&n,&m); 59 for(int i=1;i<=m;i++) 60 { 61 scanf("%d%d",&u,&v); 62 add(u,v); 63 add(v,u); 64 } 65 for(int i=1;i<=n;i++)//求每个连通块的割点 66 { 67 if(num[i]==0) 68 tarjan(i,i); 69 } 70 printf("%d\n",cnt); 71 for(int i=1;i<=n;i++) 72 { 73 if(ans[i]) 74 printf("%d ",i); 75 } 76 return 0; 77 }
需要注意几件事:
1.若该点v已被访问过,又通过边(u,v)来到v,v的low值是low[v]值与dfn[n]的最小值,而不是low[v]与low[u]的最小值,原因可以参看这里。
2.给的图不一定是连通图,所以要注意是求每个连通块的割点。
割边
与割点类似,使用tarjan算法。但是判定与写法略有不同。
判定:若搜索树上存在x的一个子节点y,满足dfn[x]<low[y],则边(x,y)为一个割边。
因为dfn[x]<low[y]说明从y出发,在不经过(x,y)的情况下,无论走哪条边都无法到达x,所以将边(x,y)删掉后,原图G将分裂为两个不相连的子图。
·为什么没有等于号:在相等时说明存在其他边链接x与y。删除该边对原图连通性没有贡献。但是割点中若两值相等则证明y不经过(x,y)到达的时间戳最小的点依旧是x,则删除x可以使y与其他点分离。
·重边的处理方法:x是y的父亲(不是父亲的话重边对算法无影响),当x与y点中间有多条边的时候,(x,y)一定不是桥。在这些重复的边中,只有一条边算是搜索树上的边,故此时可以用dfn[x]来更新low[y](若没有重边,是不能用父节点的dfn更新子节点的low值的)。算法进阶指南中提供了一种使用临接表存图解决的方法,这里介绍另一种解决方法(来自博客):
设一个布尔值,如果是父节点第一次不予通过并把布尔值标记为一,当有重边的时候,这条边一定会再次访问,此时发现布尔值为一,直接更新。
下面是代码(洛谷没有割边模板题,可以自行寻找):
1 void tarjan(int u, int fa) 2 { 3 dfn[u] = low[u] = ++dfs_clock; 4 bool flag = false; 5 for(int i=0; i<g[u].size(); ++i) 6 { 7 int v = g[u][i]; 8 if(v==fa && !flag)//如果有重边,那么边(u,v)被存了两次,所以,如果第二次访问,就让他通过 9 { 10 flag = true; 11 continue; 12 } 13 if(dfn[v]==0) 14 tarjan(v,u); 15 low[u] = min(low[u],low[v]); 16 if(low[v] > dfn[u]) 17 cntCut++; 18 } 19 }
以上。
11.28 周六
今天又是考试!果不其然凉凉又迟到了...等等....是就没来啊!一觉睡到16点就过分了啊!
今天的考试难度较为简单,A了1道题,第二道其实60分知道怎么做,但01背包调!不!出!来!
PS:先写的贪心但是无法证明于是用了动归,谁知道贪心居然是对的!
果然我只有脚而已。
接下来继续昨天内容。
此乃ydy负责部分内容:
强连通分量
你以为我要讲缩点?
错啦!其实讲缩点之前还要学强连通分量+拓扑排序+DP哒!
....
好吧一个一个来
首先缩点,顾名思义,就是将有向有环图上的环缩成一个一个点。而求环就是缩点要做的第一件事。
所谓的环,就是我们所说的强连通分量:即在有向图中的一个强连通子图(子图上的点都能互相到达)
求强连通分量自然可以想到使用Tarjan算法。
靠好累啊!!!
11.29 周日
缩点
我 好 慢 啊
洛谷P3387 缩点
题目背景
缩点+DP
题目描述
给定一个 n 个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式
第一行两个正整数 n,m
第二行 n 个整数,依次代表点权
第三至 m+2行,每行两个整数 u,v,表示一条 u→v 的有向边。
输出格式
共一行,最大的点权之和。
输入输出样例
输入 #12 2 1 1 1 2 2 1输出 #12说明/提示
【数据范围】
对于 100% 的数据,1≤n≤104,1≤m≤105,点权∈[0,1000]算法:Tarjan 缩点 + DAGdp