算法競賽——進階指南——acwing 364. 網絡 e-DCC + LCA O(M+NQ)做法

先把圖進行e-DCC縮點,得到若干橋連接的e-DCC。

給的圖是聯通的,所以得到的e-DCC圖是一棵樹。

初始每個樹邊都是橋。

每家一個邊x-y。會讓樹上  c[x] - >  c[y] (c[x]: x點所在的e-DCC編號) 點路徑連成環,路徑上的樹邊所表示的橋均會變成非橋邊。

所以我們可以每次跑x-y路徑上的所有邊,若某個邊  a-b,是橋邊,則讓總橋數ans-1,且讓這條邊標記爲非橋邊。

每次路徑最長爲N,最多Q次,複雜度O(M+N*Q)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 4e5+7;

int head[M],cnt=1,head_c[M],cnt_c=1;
struct EDGE{int to,nxt,w;}ee[M*2],ee_c[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
void add_c(int x,int y,int w){ee_c[++cnt_c].nxt=head_c[x],ee_c[cnt_c].w=w,ee_c[cnt_c].to=y,head_c[x]=cnt_c;}
int dfn[M],low[M],nm;
bool bridge[M];
void tarjan(int x,int in_edge)
{
	dfn[x]=low[x]=++nm;
	for(int i=head[x];i;i=ee[i].nxt)
	{
		int y=ee[i].to;
		if(!dfn[y])
		{
			tarjan(y,i);
			low[x]=min(low[x],low[y]);
			if(low[y]>dfn[x])bridge[i]=bridge[i^1]=true;
		}
		else if(i!=(in_edge^1))low[x]=min(low[x],dfn[y]);
	}
}
int c[M],fa[M];
int tg[M];//這條邊是否時橋邊 
int sz;//e-DCC個數 
int get(int x)
{
	if(x==fa[x])return x;
	return fa[x]=get(fa[x]);
}
void mg(int x,int y)
{
	int gx=get(x),gy=get(y);
	if(gx!=gy)fa[gx]=gy;
}
void dfs(int x)
{
	c[x]=sz;
	for(int i=head[x];i;i=ee[i].nxt)
	{
		int y=ee[i].to;
		if(c[y]||bridge[i])continue;
		dfs(y);
	}
}

int n,m;
int d[M];//深度
int f[M][21];//點i的  (1<<j)輩祖先 
void bfs()
{
	queue<int>q;
	for(int i=1;i<=n;i++)d[i]=0;
	q.push(1);d[1]=1;
	while(q.size())
	{
		int x=q.front();q.pop();
		for(int i=head_c[x];i;i=ee_c[i].nxt)
		{
			int y=ee_c[i].to,w=ee_c[i].w;
			if(d[y])continue;
			d[y]=d[x]+1;f[y][0]=x;q.push(y);
		}
	}
	for(int k=1;k<=20;k++)
		for(int i=1;i<=n;i++)
			f[i][k]=f[f[i][k-1]][k-1];
}
int lca(int x,int y)
{
	if(d[x]>d[y])swap(x,y);
	for(int i=20;i>=0;i--)
		if(d[f[y][i]]>=d[x])y=f[y][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)
		if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int u,v,T=0;
  	while(cin>>n>>m)
  	{
  		if(n==0)break;++T;
		cnt=cnt_c=1;sz=0;
		for(int i=1;i<=4*n;i++)head[i]=head_c[i]=bridge[i]=dfn[i]=c[i]=0,fa[i]=i;
  		for(int i=1;i<=m;i++)cin>>u>>v,add(u,v,1),add(v,u,1);
  		int q;
  		tarjan(1,0);
		cin>>q;
  		for(int i=1;i<=n;i++)
  		{
  			if(c[i])continue;
  			++sz;dfs(i);
		}
		for(int i=1;i<=n;i++){
	//		cout<<i<<"  -  "<<c[i]<<"  -= -=  "<<endl;
		}
		int ans=0;//橋邊個數 
		for(int i=2;i<=cnt;i+=2)
		{
			int x=c[ee[i].to],y=c[ee[i^1].to];
			if(bridge[i])add_c(x,y,1),add_c(y,x,1),tg[cnt_c]=tg[cnt_c^1]=1,ans++;
			//cout<<cnt_c<<"   - -=- =-  "<<x<<" "<<y<<endl;;
		}
	//	cout<<"OKKKK          "<<ans<<endl;
		bfs();
		cout<<"Case "<<T<<":"<<endl;
  		while(q--)
  		{
  			int x,y;
  			cin>>u>>v;
  			x=c[u],y=c[v];
  			int tp=lca(x,y);
  		//	cout<<tp<<" - -=  "<<endl;
  			while(x!=tp){
  				for(int i=head_c[x];i;i=ee_c[i].nxt){
  					int yx=ee_c[i].to;
  					if(yx==f[x][0])
  					{
  						if(tg[i]==1)tg[i]=tg[i^1]=0,ans--;
  						x=f[x][0];
  						break;
					}
				}
			}
			while(y!=tp){
  				for(int i=head_c[y];i;i=ee_c[i].nxt){
  					int yy=ee_c[i].to;
  					if(yy==f[y][0])
  					{
  						if(tg[i]==1)tg[i]=tg[i^1]=0,ans--;
  						y=f[y][0];
  						break;
					}
				}
			}
			cout<<ans<<"\n";
		}
		cout<<endl;
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章