两道和Tarjan有关的图论题

重要道路 
Time Limits: 1000 ms  Memory Limits: 262144 KB     


Description


给定一个无向图 G,对于其中的一条边(u,v),将它删除之后会使得从 1 到 n 的最短路长度增加,那么这条边被称为“重要道路”。
求 G 中所有的重要道路。


Input


第一行两个整数 n,m,分别代表点数和边数。
接下来 m 行,每行三个整数 u,v,c,代表权值为 c 的无向边(u,v)。可能有重 边,但没有自环。保证 1 号点和 n 号点是连通的。


Output


第一行一个整数 k,代表重要道路的数量。
下一行升序输出 k 个数代表重要道路的编号(按输入顺序从 1 到 m)。


Sample Input


6 7
1 2 1
2 3 1
2 5 3
1 3 2
3 5 1
2 4 1
5 6 2


Sample Output


2
5 7


Data Constraint


20%的数据,n,m<=10。
另外 20%的数据,m=n-1。

100%的数据,1<=n<=20000,1<=m,c<=100000。


思路::先是最短路求出dis[]

然后什么样的路会可能是重要道路呢?

可以这么说,对于点i,有点j可以走到点i

若有d[i] == d[j] + EdgeValue[i][j]

那么边(i,j)可能是重要道路

我们把可能是重要道路的路扔进一个新图,然后在新图上找割边,问题就解决了。

#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
         using namespace std; const int N=50010,INF=0x7fffffff; int i,j,k,n,m,T=1; struct heap{ int num,dis; }z[N]; struct edge{ int y,l,z,r; }f[200010*2]; int ans[200010]; int g[N]; void ins(int a,int b,int c){ f[++T].y=b;f[T].l=g[a];g[a]=T;f[T].z=c;f[T].r=i; f[++T].y=a;f[T].l=g[b];g[b]=T;f[T].z=c;f[T].r=i; } int dis[N],v[200010*2],tt=0;//v remark the edge f's k int pos[N];//return the num in heap of point int ret; void up(int k){ int i=k,j; while(i!=1&&i!=-1&&z[i/2].dis>z[i].dis){ z[0]=z[i];z[i]=z[i/2];z[i/2]=z[0]; pos[z[i].num]=i; pos[z[i/2].num]=i/2; i/=2; } } void push(int num,int dis){ z[++tt].num=num; z[tt].dis=dis; pos[num]=tt; up(tt); } int get(){ int re=z[1].num; pos[re]=-1; z[1]=z[tt]; pos[z[tt].num]=1; tt--; int i=1; while(i*2<=tt){ int tem=-1; if(i*2+1>tt||z[i*2].dis 
        
          tt||tem==-1)break;else{ z[0]=z[tem];z[tem]=z[i];z[i]=z[0]; pos[z[tem].num]=tem; pos[z[i].num]=i; i=tem; } } return re; } void dij(){ int i,j,k; for(i=1;i<=n-1;i++){ int j=get(); for(k=g[j];k;k=f[k].l){ if(dis[j]+f[k].z 
         
           dfn[p]){ ans[++w]=f[k].r; } } } int main(){ cin>>n>>m; for(i=2;i<=n;i++)push(i,INF),dis[i]=INF; for(i=1;i<=m;i++){ int u,v,c; cin>>u>>v>>c; ins(u,v,c); if(v==1){ v=u;u=1; } if(u==1&&z[pos[v]].dis>c){ z[pos[v]].dis=c; up(pos[v]); dis[v]=c; } } dij(); memset(v,0,sizeof(v)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); // for(i=1;i<=n;i++)cout< 
          
            < 
            
 

出行 (Standard IO)
Time Limits: 1000 ms  Memory Limits: 262144 KB     


Description


 
【问题の描述】
 
上海又叫做申城,那里的地铁一直是很拥挤的。鸡腿从张江去邯郸本部的路上常常是挤得……瘦了一圈。但是呢今天因为没有学霸的碾压,鸡腿心情很好决定要出去玩。为了避免遇上残酷的拥挤的地铁线,鸡腿想要查找一些路线中的必经地点,请你来告诉他吧。
上海的地铁被描述为N个点M条边的一个无向图,每次鸡腿会告诉你他在第S条路上,想要到第T条路上(别问我为什么是路上……),你能告诉他有多少个途中的点是他必须要经过的吗?


Input


第一行输入两个正整数N,M表示无向图的点数和边数。
 
第2到M+1行每行两个整数X、Y,表示X节点和Y节点之间有一条无向边。(数据保证无重边自环)
接下来一行输入一个整数Q,表示有Q个询问
接下来Q行每行两个正整数S、T,表示询问第S条边到第T条边有多少点一定会经过。


Output


对于每个询问输出一行一个整数表示至少有Ans个点一定会经过。


Sample Input


5 6
1 2
1 3
2 3
3 4
4 5
3 5
2
2 3
2 4


Sample Output


0
1


Data Constraint


对于20%的数据1 ≤ N ≤ 10^2,1 ≤ M ≤ 10^3,1 ≤ Q ≤ 10^3;
对于40%的数据 1 ≤ N ≤ 10^3,1 ≤ M ≤ 10^4,1 ≤ Q ≤ 10^3;
对于100%的数据 1 ≤ N ≤ 10^4,1 ≤ M ≤ 10^5,1 ≤ Q ≤ 10^4,0<Xi,Yi ≤ N, 0< S,T ≤ M。

这题他是从边出发到边的。应联想到化边为点。
是人都会想到要用tarjan,我们应找割点,然后呢?
对,化边集为点,将双联通的边集化为一个点,然后割点单独一个点
新图必定是一个边点隔一个割点的一颗数。
那么问题转化成求树上两点距离,这个可以logN实现,用倍增求LCA。

#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        
#include
        
        
         
         

using namespace std;

const int N=200010,M=200010;

int g[N],T,n,m,i,j,k,d[N],indez[31];

struct edge{
	int y,l,re;
}f[M*2];

inline void ins(int a, int b,int r){
	f[++T].y=b;f[T].l=g[a];g[a]=T;f[T].re=r;
	f[++T].y=a;f[T].l=g[b];g[b]=T;f[T].re=r;	

}


int dfn[N],low[N],num=0,ti=0,now,stack[M],forst,edgN[M];

int Ng[N];

bool cut[N];

edge Nf[N];

void Nins(int a,int b){
	Nf[++T].y=b;Nf[T].l=Ng[a];Ng[a]=T;
	Nf[++T].y=a;Nf[T].l=Ng[b];Ng[b]=T;
}	

int _n[N] ;

bool v[N+M];

int R_V;

int findd=0;
//edge nf[M*2];

void dfs(int po,int fa){//aimed at Making New Graph && Getting edge into point , marking every point , N_G point needn't mark 
 	int i,j,k;
	dfn[po]=low[po]=++ti;
	for(k=g[po];k;k=f[k].l){
		j=f[k].y;
		bool p=(dfn[j]==0);
		if(dfn[j]==0){
			dfs(j,po);
			if(po==1)findd++;
			if(low[j]>=dfn[po]&&(po!=1||findd>1)){
				cut[po]=1;
				R_V=po;
			}
		}
		if(fa!=j&&p)low[po]=min(low[po],low[j]);else
			if(fa!=j)low[po]=min(dfn[j],low[po]);
	}
}

bool jud[N];

int _d[N],ford;

void _dfs(int po,int fa){
	int i,j,k;
	dfn[po]=low[po]=++ti;
	jud[po]=1;
	for(k=g[po];k;k=f[k].l){
		j=f[k].y;
		bool p=(dfn[j]==0);	
		if(v[f[k].re]==0){
			v[f[k].re]=1;
			stack[++forst]=f[k].re;
		}
		if(jud[j]==0){
			_dfs(j,po);
			if(low[j]>=dfn[po]&&(po!=1||findd>1)){
				if(_n[po]==0)_n[po]=++num;
				++num;
				while(stack[forst+1]!=f[k].re)edgN[stack[forst--]]=num;
				Nins(num,_n[po]);
				while(low[_d[ford]]>=dfn[po])Nins(_n[_d[ford--]],num);
			}
		}
		if(fa!=j&&p)low[po]=min(low[po],low[j]);else
			if(fa!=j)low[po]=min(dfn[j],low[po]);
	}
	if(cut[po])_d[++ford]=po;
}

int dep[N+M],l[M+N][63];

int Pre_LCA(int po,int depth,int fa){
	v[po]=1;
	dep[po]=depth;
	l[po][0]=fa;
	for(int j=1;j<=30&&indez[j]<=depth-1;j++){
		l[po][j]=l[l[po][j-1]][j-1];
	}
	for(int k=Ng[po];k;k=Nf[k].l){
		int j=Nf[k].y;
		if(!v[j]){
			Pre_LCA(j,depth+1,po);
		}
	}
}

int LCA(int a,int b){
	int sub=abs(dep[a]-dep[b]),x,y;
	if(dep[a]>dep[b])x=a,y=b;else x=b,y=a;
	int j=0,ret=0;
	while(sub){
		if(sub%2){
			ret+=indez[j];
			x=l[x][j];
		}
		++j;
		sub/=2;
	}
	int lim,i;
	for(i=0;i<=30&&indez[i]<=dep[x]-1;i++);
	lim=--i;
	if(lim<0||x==y)return ret;else{
		while(i>=0){	
			for(i=lim;i>=0;i--){
				if(l[x][i]!=l[y][i]){
					x=l[x][i],y=l[y][i];
					ret+=2*indez[i];
				}			
			}
		}	
		++ret;
		x=l[x][0];
		++ret;
		y=l[y][0];
	}
	if(x!=y||x<=0||y<=0)return 0;
	return ret;
	
}

int rec[N];

int main(){
	indez[0]=1;
	for(i=1;i<=30;i++)indez[i]=indez[i-1]*2;
	cin>>n>>m;
	for(i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		ins(x,y,i);
	}
	T=0;
	memset(v,0,sizeof(v));
	for(i=1;i<=n;i++)if(dfn[i]==0)dfs(i,0);
	memset(jud,0,sizeof(jud));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	ti=0;
	for(i=1;i<=n;i++)if(dfn[i]==0){	
		_dfs(i,0);
		++num;
		while(forst)edgN[stack[forst--]]=num;
		while(ford)Nins(_n[_d[ford--]],num);
	}
	memset(v,0,sizeof(v));
	for(i=1;i<=num;i++)if(v[i]==0)Pre_LCA(i,1,0);//po depth fa
	int Q;
	cin>>Q;
	while(Q--){
		int s,t;
		scanf("%d%d",&s,&t);
		int ans=LCA(edgN[s],edgN[t])/2;
		cout<
         
         
           < 
           
         
        
        
       
       
      
      
     
     



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值