FJU 1154 LCA+tarjan

Fat Brother And His Love

Time Limit: 2 Sec   Memory Limit: 128 MB

Description

Aswe know, fat Brother and his goddess is in a same city. The city isconsist of N locations and the N locations is connected by M roads.Fat Brother has a crush on his goddess, but when he knew that thegoddess want to date with other boy, he is reluctant. So he decidedto stop their dating, then he go to find a single giant bomb from thewarehouse. He can use this giant bomb blew up a road. Fat Brotherwondered whether he can separate goddess and other boy by blowing upa road. Fat Brother gives Q questions that each query is given twonumbers u and v which means the number of the location of the goddessand the boy. If fat Brother can separate goddess and other boy,output a line “Hei!Hei!Hei!” and a line integer denoting the waysto separate them. If fat Brother can't, output a line “No! Ichoose to go die!”.

Youcan think the city which Fat Brother, goddess and boys are in is anundirected graph, and the graph is always connected in the beginning.

Ifyou can't understand it, you should observate the sample Input andsample Output

Input

There are multiple test cases. The first line of input contains an integer T (T <= 25) indicating the number of test cases. For each test case:

The first line contains three integer N, M and Q denoting there are N locations and M roads in the city. The Q denoting there are Q questions. (1 <= N <= 100000, 1 <= m <= 100000, 1 <= Q <= 100000)

Each of the 2…M + 1 lines contains two integers u and v denoting there is a undirected road between u and v.

Each of the M + 2 … M + 1 + Q lines contains two integer u and v denoting the fat Brother's question.

Output

For each case, output acconding to Title Description.

Sample Input

2
9 11 19
1 2
1 3
2 3
2 4
4 5
4 6
5 6
6 7
7 8
7 9
8 9
1 2
1 3
2 3
2 4
4 5
4 6
5 6
6 7
7 8
7 9
8 9
1 4
4 7
8 5
3 6
9 4
1 6
7 3
2 9
10 9 8
1 2
1 3
3 4
4 5
3 6
6 7
3 8
8 9
8 10
1 5
1 2
7 5
9 4
2 3
6 2
4 1
3 9

Sample Output

No! I choose to go die!
No! I choose to go die!
No! I choose to go die!
Hei!Hei!Hei!
1
No! I choose to go die!
No! I choose to go die!
No! I choose to go die!
Hei!Hei!Hei!
1
No! I choose to go die!
No! I choose to go die!
No! I choose to go die!
Hei!Hei!Hei!
1
Hei!Hei!Hei!
1
Hei!Hei!Hei!
1
Hei!Hei!Hei!
1
Hei!Hei!Hei!
1
Hei!Hei!Hei!
1
Hei!Hei!Hei!
2
Hei!Hei!Hei!
2
Hei!Hei!Hei!
3
Hei!Hei!Hei!
1
Hei!Hei!Hei!
4
Hei!Hei!Hei!
3
Hei!Hei!Hei!
2
Hei!Hei!Hei!
3
Hei!Hei!Hei!
2
Hei!Hei!Hei!
2

题意:给定n个点、m条边的无向连通图,有q个询问,问有多少条边是从点u到点v必经的。有解输出“Hei!Hei!Hei!”和边数,无解输出“No! I choose to go die!”。


题解:先tarjan缩点建图   

然后做一遍lca

那么两点同一个联通分量里面肯定是go die!

否则我们找到两个点的lca

然后求一下深度即可



#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int head[100005],cnt,tot,inde,top,tots,dfn[200005],low[200005],ef[200005],bccnum,bcc[200005],st[200005],heads[100005],dp[100005];
struct node{
	int fro,to,nex,yes;
}edge[200005],edges[200005];
const int MAXN = 100010;  
int rmq[2*MAXN];//rmq数组,就是欧拉序列对应的深度序列  
struct ST  
{  
    int mm[2*MAXN];  
    int dp[2*MAXN][20];//最小值对应的下标  
    void init(int n)  
    {  
        mm[0] = -1;  
        for(int i = 1;i <= n;i++)  
        {  
            mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];  
            dp[i][0] = i;  
        }  
        for(int j = 1; j <= mm[n];j++)  
            for(int i = 1; i + (1<<j) - 1 <= n; i++)  
                dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];  
    }  
    int query(int a,int b)//查询[a,b]之间最小值的下标  
    {  
        if(a > b)swap(a,b);  
        int k = mm[b-a+1];  
        return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];  
    }  
}sts;  
int F[MAXN*2];//欧拉序列,就是dfs遍历的顺序,长度为2*n-1,下标从1开始  
int P[MAXN];//P[i]表示点i在F中第一次出现的位置  
void add(int u,int v){
	edge[tot].fro=u;
	edge[tot].to=v;
	edge[tot].nex=head[u];
	edge[tot].yes=0;
	head[u]=tot++;
}
void dfs(int u,int pre,int dep)  
{  
	dp[u]=dep;
    F[++cnt] = u;  
    rmq[cnt] = dep;  
    P[u] = cnt;  
    for(int i = heads[u];i != -1;i = edges[i].nex)  
    {  
        int v = edges[i].to;  
        if(v == pre)continue;  
        dfs(v,u,dep+1);  
        F[++cnt] = u;  
        rmq[cnt] = dep;  
    }  
}  
void LCA_init(int root,int nodenum)//查询LCA前的初始化  
{  
    cnt = 0;  
    dfs(root,root,0);  
    sts.init(2*nodenum-1);  
}  
int query_lca(int u,int v)//查询u,v的lca编号  
{  
    return F[sts.query(P[u],P[v])];  
}  
void adds(int u,int v){
	edges[tots].fro=u;
	edges[tots].to=v;
	edges[tots].nex=heads[u];
	edges[tots].yes=0;
	heads[u]=tots++;
}
void tarjan(int root){//求强连通分量  
    dfn[root]=low[root]=++inde;  
    st[++top]=root;   
    for(int i=head[root];~i;i=edge[i].nex){  
        int v=edge[i].to;  
        if(ef[i])continue;  
        ef[i]=ef[i^1]=1;  
        if(!dfn[v]){  
            tarjan(v);  
            low[root]=min(low[root],low[v]);
            if(dfn[root]<low[v]){
            	edge[i].yes=edge[i^1].yes=1;
            }
        }  
        else low[root]=min(low[root],dfn[v]);  
    }  
    if(low[root]==dfn[root]){  
        bccnum++;  
        for(;;){  
            int x=st[top--];  
            bcc[x]=bccnum;  
            if(x==root)break;  
        }  
    }  
}  
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int x,y,n,m,q,i,j;
		tot=0;
		bccnum=0;
		top=0;
		inde=0;
		tots=0;
		memset(head,-1,sizeof(head));
		memset(heads,-1,sizeof(heads));
		memset(dfn,0,sizeof(dfn));  
        memset(bcc,0,sizeof(bcc));  
        memset(low,0,sizeof(low));
		memset(ef,0,sizeof(ef));  
		scanf("%d%d%d",&n,&m,&q);
		for(i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			add(x,y);
			add(y,x);
		}
		tarjan(1);
		for(i=0;i<tot;i+=2){
			if(edge[i].yes){
				adds(bcc[edge[i].fro],bcc[edge[i].to]);
				adds(bcc[edge[i].to],bcc[edge[i].fro]);
			}
		}
		LCA_init(1,bccnum);
		while(q--){
			int x,y;
			scanf("%d%d",&x,&y);
			if(bcc[x]==bcc[y]){
				printf("No! I choose to go die!\n");
			}
			else{
				int zu=query_lca(bcc[x],bcc[y]);
				printf("Hei!Hei!Hei!\n%d\n",dp[bcc[x]]+dp[bcc[y]]-2*dp[zu]);
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值