• 强连通分量填坑记


    关于为什么要填坑

    因为有坑啊

    本宝宝深觉自己的蒟蒻,写了道坑题被爆了

    具体哪道题不给你说

    于是本蒟蒻决定重新学习强连通分量(直播中

    9.15
    10:01
    开始啃书

    放上定义

    在有向图G中,如果两个顶点间至少存在一条互相可达路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。

    所以我们要搞的就是在有向图中找强连通子图

    10:20
    我觉得我可以写题写板了。
    主要还是写tarjan

    【性质】

    如果u是某个强连通分量的根,那么:
    (1)u不存在路径可以返回到它的祖先。
    (2)u的子树也不存在路径可以返回到u的祖先。

    【算法描述】

    (1)我们先对每一个顶点判断,如果已经被运行过了,则不动,否则进行拓展
    (2)对于每一个点,我们设2个值来表示(dfn和low){low[i]表示结点i的根结点的dfn值},dfn则是结点i的时间戳
    (3)在第一次顺序遍历时,dfn[u]=low[u]=++cnt,初始化序列
    (4)然后将遍历到的点压入栈,并且置为已做过
    (5)在整个图中枚举一条以u为右端点的边(u,v),然后判断左端点是否已经入栈,如果已经入栈,则修改low[u]:low[u]=max(low[u],dfn(v)),如果没入栈,则对其进行递归处理,返回时low[u]=min(low[u],low[v])
    (6)当出现dfn[u]==low[u]时,则表明已经出现一个强联通分量,依次弹出并消除标记即可,此时cnt++,则该作用为统计联通块数量
    11:12
    我去,在下输了
    还没有弄出来的绝望感
    11:35
    我终于搞出来了个基础款...

    #include<bits/stdc++.h>
    using namespace std;
    struct node{
    	int v,nxt;
    }e[1000101];
    int fir[1000101]={0},cnt=0;
    int dfn[1001001]={0},low[1000101]={0},kuai[1001011]={0},tot=0,ans=0;
    bool vis[100101]={0},inst[100101];
    int st[1001011]={0},top=0;
    void add(int uu,int vv){
    	e[++cnt].v=vv;e[cnt].nxt=fir[uu];fir[uu]=cnt;
    }
    int n,m;
    void tarjan(int u){
    	vis[u]=1;low[u]=dfn[u]=++tot;
    	st[top++]=u;inst[u]=true;
    	for(int i=fir[u];i;i=e[i].nxt){
    		int vv=e[i].v;//cout<<vv<<' '<<u<<endl;
    		if(!dfn[vv]){
                        tarjan(vv);
                        low[u]=min(low[u],low[vv]);
                    }
                    else if(inst[vv])low[u]=min(low[u],dfn[vv]);
    	}
    	if(dfn[u]==low[u]){
    		ans++;//cout<<u<<endl;
    		int x;
    		while(x!=u){
    			x=st[top--];
    			inst[x]=false;
    			kuai[x]=ans;
    		}
    	}
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    	}
    	for(int i=1;i<=n;i++)
    		if(!dfn[i])tarjan(i);
    	cout<<ans;
    	return 0;
    }
    

    可见我这个人有多么的蒟蒻
    对dfs结构都还有点懵
    ε=(´ο`*)))唉,或许省一已经无望了...

    12:49
    吃饱喝足了,继续
    先上个例题

    受欢迎的牛

    链接
    强连通分量+缩点,板子嘛
    之后统计出度为0的块
    13:35
    好了
    上加点料的板子

    #include<bits/stdc++.h>
    using namespace std;
    struct node{
    	int u,v,nxt;
    }e[200011];
    int fir[100101]={0},cnt=0,p[100101]={0},anss=0;
    int dfn[100101]={0},low[100101]={0},kuai[101011]={0},tot=0,ans=0;
    bool vis[100101]={0},inst[100101];
    int st[100011]={0},top=0;
    void add(int uu,int vv){
    	e[++cnt].v=vv;e[cnt].u=uu;e[cnt].nxt=fir[uu];fir[uu]=cnt;
    }
    int n,m;
    void tarjan(int u){
    	vis[u]=1;low[u]=dfn[u]=++tot;
    	st[top++]=u;inst[u]=true;
    	for(int i=fir[u];i;i=e[i].nxt){
    		int vv=e[i].v;//cout<<vv<<' '<<u<<endl;
    		if(!dfn[vv]){
                tarjan(vv);
                low[u]=min(low[u],low[vv]);
            }
            else if(inst[vv])low[u]=min(low[u],dfn[vv]);
    	}
    	if(dfn[u]==low[u]){
    		ans++;//cout<<u<<endl;
    		int x;
    		while(x!=u&&top>0){
    			x=st[--top];
    			inst[x]=false;
    			kuai[x]=ans;
    		}
    	}
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    	}
    	for(int i=1;i<=n;i++)
    		if(!dfn[i])tarjan(i);
    	for(int i=1;i<=m;i++){//p用来统计出度
    		if(kuai[e[i].u]!=kuai[e[i].v])p[kuai[e[i].u]]++;
    	}
    	int lala=0;
    	for(int i=1;i<=n;i++){
    		//cout<<kuai[i]<<' ';
    		if(lala!=0&&kuai[i]!=lala&&p[kuai[i]]==0){
    			anss=0;break;
    		}
    		if(p[kuai[i]]==0&&kuai[i]>0)anss++,lala=kuai[i];
    	}
    	if(n==1)anss=0;
    	cout<<anss;
    	return 0;
    }
    

    有坑啊,为啥95<笑哭>
    13:42
    吃晚饭加补课去了,明天继续更新
    17:38
    咳咳,偷偷出来摸鱼
    预告下一题

    消息扩散

    链接
    这次不是特别容易想到解法
    10:04
    好吧其实和上一道题一般弱智
    统计入度为0的块的个数
    模板水题一道

    using namespace std;
    struct node{
    	int u,v,nxt;
    }e[1000011];
    int fir[1000101]={0},cnt=0,p[1000101]={0},anss=0;
    int dfn[1001001]={0},low[1000101]={0},kuai[1010011]={0},tot=0,ans=0;
    bool vis[1000101]={0},inst[1000101];
    int st[1000011]={0},top=0;
    void add(int uu,int vv){
    	e[++cnt].v=vv;e[cnt].u=uu;e[cnt].nxt=fir[uu];fir[uu]=cnt;
    }
    int n,m;
    void tarjan(int u){
    	vis[u]=1;low[u]=dfn[u]=++tot;
    	st[top++]=u;inst[u]=true;
    	for(int i=fir[u];i;i=e[i].nxt){
    		int vv=e[i].v;//cout<<vv<<' '<<u<<endl;
    		if(!dfn[vv]){
                tarjan(vv);
                low[u]=min(low[u],low[vv]);
            }
            else if(inst[vv])low[u]=min(low[u],dfn[vv]);
    	}
    	if(dfn[u]==low[u]){
    		ans++;//cout<<u<<endl;
    		int x;
    		while(x!=u&&top>0){
    			x=st[--top];
    			inst[x]=false;
    			kuai[x]=ans;
    		}
    	}
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    	}
    	for(int i=1;i<=n;i++)
    		if(!dfn[i])tarjan(i);
    	for(int i=1;i<=m;i++){
    		if(kuai[e[i].u]!=kuai[e[i].v])p[kuai[e[i].v]]++;
    	}
    	for(int i=1;i<=ans;i++)
    		if(p[i]==0)
    			anss++;
    	cout<<anss;
    	return 0;
    }
    

    比受欢迎的牛还水

    10:06
    再附上一道缩点最短路的水题

    正则表达式

    链接
    先说我的思路并不是最优的,理论上重新建图或许跑得更快
    但这道题也可以说相当模板了,没有什么好说的,这是最后一道水题

    #include<bits/stdc++.h>
    using namespace std;
    struct node{
    	int u,v,w,nxt;
    }e[2000011];
    int fir[1001001]={0},cnt=0,p[1000101]={0},anss=0;
    int dfn[1001001]={0},low[1001001]={0},kuai[1010011]={0},tot=0,ans=0;
    bool vis[1000101]={0},inst[1000101];
    int st[1000011]={0},top=0;
    void add(int uu,int vv,int ww){
    	e[++cnt].v=vv;e[cnt].u=uu;e[cnt].w=ww;e[cnt].nxt=fir[uu];fir[uu]=cnt;
    }
    int n,m;
    void tarjan(int u){
    	vis[u]=1;low[u]=dfn[u]=++tot;
    	st[top++]=u;inst[u]=true;
    	for(int i=fir[u];i;i=e[i].nxt){
    		int vv=e[i].v;//cout<<vv<<' '<<u<<endl;
    		if(!dfn[vv]){
                tarjan(vv);
                low[u]=min(low[u],low[vv]);
            }
            else if(inst[vv])low[u]=min(low[u],dfn[vv]);
    	}
    	if(dfn[u]==low[u]){
    		ans++;//cout<<u<<endl;
    		int x;
    		while(x!=u&&top>0){
    			x=st[--top];
    			inst[x]=false;
    			kuai[x]=ans;
    		}
    	}
    }
    int dis[1000110];
    void spfa(int st){
    	queue<int> q;
    	for(int i=1;i<=n;i++)dis[i]=0x3f3f3f3f,vis[i]=0;
    	dis[st]=0;q.push(st);vis[st]=1;
    	while(!q.empty()){
    		int x=q.front();q.pop();vis[x]=0;
    		for(int i=fir[x];i;i=e[i].nxt){
    			int vv=e[i].v;//cout<<vv<<endl;
    			if(dis[vv]>dis[x]+e[i].w){
    				dis[vv]=dis[x]+e[i].w;
    				if(vis[vv]==0)vis[vv]=1,q.push(vv);
    			}
    		}
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z);
    	}
    	for(int i=1;i<=n;i++)
    		if(!dfn[i])tarjan(i);
    	for(int i=1;i<=m;i++)
    		if(kuai[e[i].u]==kuai[e[i].v])e[i].w=0;
    	spfa(1);
    	cout<<dis[n];
    	return 0;
    }
    

    10:10
    将目光投向某些紫题中
    预告下一道

    最小割

    链接
    11:12
    我错了,一如网络流时间就深似海一般像个黑洞一般消逝了
    咳咳,我选择放弃

  • 相关阅读:
    SVN菜单说明
    Jabber Software:Jabber-NET、agsXMPP与Wilefire[转]
    nuget的使用总结
    SET QUOTED_IDENTIFIER ON
    SET ANSI_NULLS ON
    SQL Server性能杀手
    How to open .ccproj in VS2010?
    Bios里,把SATA Mode Selection改为AHCI无法启动
    [转]内嵌WORD/OFFICE的WINFORM程序——DSOFRAMER使用小结
    使用EF连接现有数据库
  • 原文地址:https://www.cnblogs.com/lisuier/p/9650057.html
Copyright © 2020-2023  润新知