一本通->提高篇->图论->割点和桥:

本文深入解析图论中的割点与桥概念,通过多个实例讲解如何利用DFS算法识别并处理割点与桥,包括边双连通分量、点双连通分量的分析,以及割点在不同场景下的应用。
部署运行你感兴趣的模型镜像

一本通:
提高篇:
图论:
割点和桥:

1520:【 例 1】分离的路径
题意:如何把有桥图通过加边变成边双连通分量
如果叶子数(缩点后度为1的点)为1,则至少需要添加0条边;
否则为(叶子数+1)/ 2;

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
int n,m,cnt=1; //cnt从1开始,保证取反边 
int head[N],ver[N],nex[N],colour,top;
int dfn[N],low[N],dfstime,vis[N],du[N],col[N];
stack<int> st;
void add(int a,int b){
	ver[++cnt] = b;
	nex[cnt] = head[a];
	head[a] = cnt;
}
void read(){
	memset(head,-1,sizeof head);
	scanf("%d%d",&n,&m);
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
}
void tarjan(int x){
	dfn[x] = low[x] = ++dfstime;
	st.push(x);
	for(int i=head[x];~i;i=nex[i]){
		int y = ver[i];
		if(!vis[i^1]){
			vis[i] = 1;
			if(!dfn[y]){
				tarjan(y);
				low[x] = min(low[x],low[y]);
			}else low[x] = min(low[x],dfn[y]);
		}else vis[i] = 1;
	}
	int i;
	if(low[x] == dfn[x]){//桥不可能在多个桥双连通里,而点可能在多个点双连通里 
		colour++;
		do{
			i=st.top();st.pop();
			col[i]  = colour; //通桥双连通染色 
		}while(i!=x);
	}
}
void solve(){	
	//tarjan(1);
	for(int i=1;i<=n;i++)
	if(!dfn[i]) tarjan(i);
	
	for(int x=1;x<=n;x++){
		for(int i=head[x];~i;i=nex[i]){
			if(vis[i^1]){ //树枝边,因为dfs不考虑双向边 
				vis[i] = 0;
				int y=ver[i];
				if(col[y] != col [x]){
					du[col[x]]++; //找到叶子节点 
					du[col[y]]++;
				}
			}else vis[i] = 0;
		}
	}
	int ans = 0;
	for(int i=1;i<=colour;i++){
		if(du[i]==1) ans++;
	}
	printf("%d\n",(ans+1)/2);//新建道路的数目是把原桥删去后桥双连通缩成点后 把每两个叶子上连边即可 
	//结论:当叶子节点=1,要+的边为0,否则为(叶子数+1)/2 
}
int main(){
	read();
	solve();
	return 0;
} 

1521:【 例 2】矿场搭建
点双连通 + 分析
具体三种情况
1:特殊情况,只有一个双连通分量,割点为0,答案为 c n 2 c_n^2 cn2
2:一般情况,一个双连通分量若只有一个割点,在非割点处任意选择一个点建通道。
3:割点大于等于2,不需要再建立通道。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e3;
int n,m,cs,cnt,root,dfstime,bt;
int nex[N],head[N],ver[N];
int low[N],dfn[N],cut[N];
vector<int> block[N];
stack<int> st;
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void read(){
		for(int i=0;i<N;i++) cut[i] = dfn[i]=low[i]=nex[i] = ver[i] = 0,head[i] = -1;
		bt = dfstime = cnt = 0;
		while(st.size()) st.pop();
		for(int i = 1,x,y;i <= m;i ++){
			scanf("%d%d",&x,&y);
			n = max(n,max(x,y));//题目未给出最大点数 
			add(x,y);add(y,x);
		}	
}
void tarjan(int x,int root){
	dfn[x] = low[x] = ++dfstime;
	int tot = 0;
	st.push(x);
	for(int i=head[x];~i;i=nex[i]){
		int y = ver[i];
		if(!dfn[y]){
			tot++;
			tarjan(y,root);
			low[x] = min(low[x],low[y]);
			if((x==root&& tot>1) || (x!=root && dfn[x]<=low[y] )) cut[x] = 1;//求割点的两种判断 
			if(dfn[x] <= low[y]){
				bt ++;
				block[bt].clear();
				int top;
				do{
					top = st.top();st.pop(); 
					block[bt].push_back(top);
				}while(y!=top);
				block[bt].push_back(x);  //重点:区分于桥双,点双的点可能多次出现在点双连通中 
			}
		}else{
			low[x] = min(low[x],dfn[y]);
		}
	}
}
void solve(){
	ll res = 0,num = 1;
	for(int i=1;i<=bt;i++){
		int gdnum = 0;
		int len = block[i].size();
		for(int j =0;j<len;j++){
			if(cut[block[i][j]]==1)
				gdnum ++;
		}
		if(gdnum==0) res+=2,num = num*(len-1)*len/2;
		else if(gdnum==1) res++,num=num*(len-1);
	}
	printf("%lld %lld\n",res,num);
}
int main(){
	while(~scanf("%d",&m) && m){
		printf("Case %d: ",++cs);
		read();
		for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i,i);	
		solve();
	}
	return 0;
} 

1522:网络
割点模板

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4,M = 1e6+7;
int n,m,ans;
int cnt,head[M],nex[M],ver[M];
int low[N],dfn[N],dfstime,cut[N];
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void read(){
	int x; char y;
	memset(head,-1,sizeof head);
	for(int i=0;i<N;i++)
	low[i] = dfn[i] = cut[i] = 0;
	dfstime = cnt = ans = 0;
	string line;
	while(getline(cin,line)){
		stringstream ss(line);
		int i=0,x,u,y;
		while(ss>>u){
			if(u==0) return;
			if(i==0) x = u;
			else{
				add(x,u);add(u,x);
			}
			 i++; 
		}	
	}
}
void tarjan(int x,int root){
	dfn[x] = low[x] = ++dfstime;
	int tot = 0;
	for(int i=head[x];~i;i=nex[i]){
		int y = ver[i];
		if(!dfn[y]){
			tot++;
			tarjan(y,root);
			low[x] = min(low[x],low[y]);
			if((x==root && tot>1) || (x!=root && low[y]>= dfn[x])){
				cut[x] = 1;
			}
		}else{
			low[x] = min(low[x],dfn[y]);
		}
	}
	
}
void solve(){
	for(int i=1;i <= n;i++)
	if(!dfn[i]) tarjan(i,i);
	
	for(int i=1;i <= n;i++)
	if(cut[i]==1) ans ++;
	printf("%d\n",ans);
}
int main(){
	while(scanf("%d",&n) && n){
		read();
		solve();
	}
	return 0;
}

1523:嗅探器
找割点 + 分析
1:这个割点必须是从a到b的必经点,所以不可以是 a 或者 b。
2:另一个服务器是否在路径上,也就是b 和 a 要连通, 即 low[b] >= dfn[a]。
3:这个点是在b之前,即dfn[t o[x] ] <= dfn[b], a之后(我们从a开始就保证了)。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5,M = 1e6+7,INF = 0x3f3f3f3;
int n,m,ans=INF;
int a,b;
int cnt,head[M],nex[M],ver[M];
int low[N],dfn[N],dfstime,cut[N];
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void read(){
	scanf("%d",&n);
	int x,y;
	while(scanf("%d%d",&x,&y) && x+y!=0){
		add(x,y);add(y,x);
	}
	scanf("%d%d",&a,&b);
}
void tarjan(int x,int root){
	dfn[x] = low[x] = ++dfstime;
	int tot = 0;
	for(int i=head[x];i;i=nex[i]){
		int y = ver[i];
		if(!dfn[y]){
			tot++;
			tarjan(y,root);
			low[x] = min(low[x],low[y]);
			/*if(x!=a&&x!=b)//不是起点终点
            if(dfn[x]<=low[y]&&dfn[y]<=dfn[b]&&low[b]>=dfn[a])
            ans = min(ans,x);*/
			if(low[y] >= dfn[x] && low[b] >= dfn[a] && dfn[y] <= dfn[b] && x!=a && x!=b){
				ans = min(ans,x);
			}
		}else{
			low[x] = min(low[x],dfn[y]);
		}
	}
}
int main(){
	read();
	tarjan(a,a);
	if(ans!=INF) printf("%d\n",ans);
	else puts("No solution");
	return 0;
}

1524:旅游航道
桥模板

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5,M = 1e6+7,INF = 0x3f3f3f3;
int n,m,ans;
int tot,dfn[N],low[N],dfstime;
int cnt,head[M],nex[M],ver[M];
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void read(){
	memset(head,-1,sizeof head);
	for(int i=0;i<N;i++)
	dfn[i] = low[i] = 0;
	ans = cnt = 0;
	int x,y;
	for(int i = 1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
}
int vis[N];
void tarjan(int x,int fa){
	dfn[x] = low[x] = ++dfstime;
	for(int i=head[x];~i;i=nex[i]){
		int y = ver[i];
		if(dfn[y] && y!=fa) low[x] = min(low[x],dfn[y]);
		if(!dfn[y]){
			tarjan(y,x);			
			if(dfn[x] < low[y]) //满足条件 
			ans++;		
			low[x] = min(low[x],low[y]);
		}
	}
}
int main(){
	while(~scanf("%d%d",&n,&m) && n+m){
	read();
	for(int i=1;i<=n;i++)
	if(!dfn[i]) tarjan(i,i);
	printf("%d\n",ans);		
	}
	return 0;
}

1525:电力
找割点
1:求割点后最多能产生多少分量:
这题关键点就是i == root && son==1 的情况,这种情况如果是没有割点的,但是切除根节点后任然会被分出一个节点,所以我们针对这种情况改变代码。
如果原始cut[i]=0,表示i是孤立的一点,此时cut[i]-1=-1.
如果原始cut[i]=1,表示i为根且有一个儿子,此时cut[i]-1=0.
如果原始cut[i]>=2,表示i为根且分割了>=2个儿子,此时cut[i]-1>=1.

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e4+7,M = 1e5+7;
typedef long long ll;
int n,m;
int cnt,head[M],nex[M],ver[M];
int low[N],dfn[N],dfstime,cut[N];
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void read(){
	cnt = dfstime = 0;
	memset(head,-1,sizeof head);
	for(int i=0;i < N;i++)
	cut[i] = dfn[i] = low[i] = 0;
	for(int i=1; i <= m ;i ++){
		int x,y;scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
}
void tarjan(int x,int root){
	dfn[x] = low[x] = ++dfstime;
	int tot = 0;
	for(int i=head[x];~i;i=nex[i]){
		int y = ver[i];
		if(!dfn[y]){
			tot++;
			tarjan(y,root);
			low[x] = min(low[x],low[y]);
			/*if((x==root && tot>1) || (x!=root && low[y]>= dfn[x])){
				cut[x] ++; //x能够割出多少分量 
			}*/
			if(low[y] >= dfn[x]) cut[x] ++;
		}else {
			low[x] = min(low[x],dfn[y]);
		}
	}
}
int main(){
	while(scanf("%d%d",&n,&m) && n+m !=0){
		read();
		int sum = 0,ans = -10000;
		for(int i=0; i < n;i++)
		if(!dfn[i]) tarjan(i,i),sum++,cut[i]--;
		for(int i=0;i<n;i++)
		ans = max(ans,cut[i]);
		printf("%d\n",ans+sum);
	}
	return 0;
}

1526:Blockade**
新开一篇讲解

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7,M = 1e6+7;
typedef long long ll;
int n,m;
int cnt,head[M],nex[M],ver[M];
int low[N],dfn[N],dfstime,cut[N];
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void read(){
	scanf("%d%d",&n,&m);
	for(int i=1; i <= m;i++){
		int x,y;scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
}
int size[N]; ll ans[N];
void tarjan(int x,int root){
	dfn[x] = low[x] = ++dfstime;
	int tot = 0;
	size[x] = 1;
	ll sum = 0;
	for(int i=head[x];i;i=nex[i]){
		int y = ver[i];
		if(!dfn[y]){
			tot++;
			tarjan(y,root);
			low[x] = min(low[x],low[y]);
			size[x] += size[y]; //记录x的子树节点个数 
			if((x==root && tot>1) || (x!=root && low[y]>= dfn[x])){
				cut[x] = 1;
				ans[x] += size[y]*sum;  //所有子树能提供的边数 
										//x的子树size[y]能提供的个数*前面已经提供了同属于x子树的个数和 
				sum += size[y];//sum加上这颗子树点 
			}
		}else{
			low[x] = min(low[x],dfn[y]);
		}
	}
	ans[x] += (n-1-sum)*sum+(n-1); //父亲块能提供的边数 + x自己能提供的边数 
}
int main(){
	read();
	for(int i=1;i<=n;i++)
	if(!dfn[i]) tarjan(i,i);
	
	for(int i=1;i <= n;i++){
		printf("%lld\n",ans[i]*2);
	}
	return 0;
} 

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, int max, unsigned int available_buses, int pass) { struct pci_bus *child; int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); u32 buses, i, j = 0; u16 bctl; u8 primary, secondary, subordinate; int broken = 0; bool fixed_buses; u8 fixed_sec, fixed_sub; int next_busnr; /* * Make sure the bridge is powered on to be able to access config * space of devices below it. */ pm_runtime_get_sync(&dev->dev); pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); primary = buses & 0xFF; secondary = (buses >> 8) & 0xFF; subordinate = (buses >> 16) & 0xFF; pci_dbg(dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n", secondary, subordinate, pass); if (!primary && (primary != bus->number) && secondary && subordinate) { pci_warn(dev, "Primary bus is hard wired to 0\n"); primary = bus->number; } /* Check if setup is sensible at all */ if (!pass && (primary != bus->number || secondary <= bus->number || secondary > subordinate)) { pci_info(dev, "bridge configuration invalid ([bus %02x-%02x]), reconfiguring\n", secondary, subordinate); broken = 1; } /* * Disable Master-Abort Mode during probing to avoid reporting of * bus errors in some architectures. */ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl); pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT); if ((secondary || subordinate) && !pcibios_assign_all_busses() && !is_cardbus && !broken) { unsigned int cmax, buses; /* * Bus already configured by firmware, process it in the * first pass and just note the configuration. */ if (pass) goto out; /* * The bus might already exist for two reasons: Either we * are rescanning the bus or the bus is reachable through * more than one bridge. The second case can happen with * the i450NX chipset. */ child = pci_find_bus(pci_domain_nr(bus), secondary); if (!child) { child = pci_add_new_bus(bus, dev, secondary); if (!child) goto out; child->primary = primary; pci_bus_insert_busn_res(child, secondary, subordinate); child->bridge_ctl = bctl; } buses = subordinate - secondary; cmax = pci_scan_child_bus_extend(child, buses); if (cmax > subordinate) pci_warn(dev, "bridge has subordinate %02x but max busn %02x\n", subordinate, cmax); /* Subordinate should equal child->busn_res.end */ if (subordinate > max) max = subordinate; } else { /* * We need to assign a number to this bus which we always * do in the second pass. */ if (!pass) { if (pcibios_assign_all_busses() || broken || is_cardbus) /* * Temporarily disable forwarding of the * configuration cycles on all bridges in * this bus segment to avoid possible * conflicts in the second pass between two * bridges programmed with overlapping bus * ranges. */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses & ~0xffffff); goto out; } /* Clear errors */ pci_write_config_word(dev, PCI_STATUS, 0xffff); /* Read bus numbers from EA Capability (if present) */ fixed_buses = pci_ea_fixed_busnrs(dev, &fixed_sec, &fixed_sub); if (fixed_buses) next_busnr = fixed_sec; else next_busnr = max + 1; /* * Prevent assigning a bus number that already exists. * This can happen when a bridge is hot-plugged, so in this * case we only re-scan this bus. */ child = pci_find_bus(pci_domain_nr(bus), next_busnr); if (!child) { child = pci_add_new_bus(bus, dev, next_busnr); if (!child) goto out; pci_bus_insert_busn_res(child, next_busnr, bus->busn_res.end); } max++; if (available_buses) available_buses--; buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->busn_res.start) << 8) | ((unsigned int)(child->busn_res.end) << 16); /* * yenta.c forces a secondary latency timer of 176. * Copy that behaviour here. */ if (is_cardbus) { buses &= ~0xff000000; buses |= CARDBUS_LATENCY_TIMER << 24; } /* We need to blast all three values with a single write */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); if (!is_cardbus) { child->bridge_ctl = bctl; max = pci_scan_child_bus_extend(child, available_buses); } else { /* * For CardBus bridges, we leave 4 bus numbers as * cards with a PCI-to-PCI bridge can be inserted * later. */ for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) { struct pci_bus *parent = bus; if (pci_find_bus(pci_domain_nr(bus), max+i+1)) break; while (parent->parent) { if ((!pcibios_assign_all_busses()) && (parent->busn_res.end > max) && (parent->busn_res.end <= max+i)) { j = 1; } parent = parent->parent; } if (j) { /* * Often, there are two CardBus * bridges -- try to leave one * valid bus number for each one. */ i /= 2; break; } } max += i; } /* * Set subordinate bus number to its real value. * If fixed subordinate bus number exists from EA * capability then use it. */ if (fixed_buses) max = fixed_sub; pci_bus_update_busn_res_end(child, max); pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } sprintf(child->name, (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"), pci_domain_nr(bus), child->number); /* Check that all devices are accessible */ while (bus->parent) { if ((child->busn_res.end > bus->busn_res.end) || (child->number > bus->busn_res.end) || (child->number < bus->number) || (child->busn_res.end < bus->number)) { dev_info(&dev->dev, "devices behind bridge are unusable because %pR cannot be assigned for them\n", &child->busn_res); break; } bus = bus->parent; } out: /* Clear errors in the Secondary Status Register */ pci_write_config_word(dev, PCI_SEC_STATUS, 0xffff); pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl); pm_runtime_put(&dev->dev); return max; };详细分析该接口,给出具体注释并解释原理
10-21
void br_multicast_flood(struct net_bridge_mdb_entry *mdst, struct sk_buff *skb, bool local_rcv, bool local_orig) { struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(dev); struct net_bridge_port *prev = NULL; struct net_bridge_port_group *p; struct hlist_node *rp; rp = rcu_dereference(hlist_first_rcu(&br->router_list)); p = mdst ? rcu_dereference(mdst->ports) : NULL; while (p || rp) { struct net_bridge_port *port, *lport, *rport; lport = p ? p->port : NULL; rport = hlist_entry_safe(rp, struct net_bridge_port, rlist); if ((unsigned long)lport > (unsigned long)rport) { port = lport; if (!isWiFiInterface(port->dev)&&(port->flags & BR_MULTICAST_TO_UNICAST)) { maybe_deliver_addr(lport, skb, p->eth_addr, local_orig); goto delivered; } } else { port = rport; } /*ecnt_dp_hook*/ if(ecnt_br_multicast_flood_igmp3_inline_hook(skb, mdst, br, prev, port) != ECNT_CONTINUE){ goto delivered; } prev = maybe_deliver(prev, port, skb, local_orig); if (IS_ERR(prev)) goto out; delivered: if ((unsigned long)lport >= (unsigned long)port) p = rcu_dereference(p->next); if ((unsigned long)rport >= (unsigned long)port) rp = rcu_dereference(hlist_next_rcu(rp)); } if (!prev) goto out; if (local_rcv) deliver_clone(prev, skb, local_orig); else __br_forward(prev, skb, local_orig); return; out: if(ECNT_RETURN == ecnt_br_multicast_flood_inline_hook(skb, local_rcv)) return; if (!local_rcv) kfree_skb(skb); } 为何该函数在退出while循环后 捕获不到br-lan转发到物理接口的ns报文
最新发布
10-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值