题意:
给出一个无向图。
问删去每一条边后,是否出现一对(u,v) st 删去这条边后,u和v不连通,且u<v,若有多对,输出u最大的,然后v最小的。
数据范围10^5
思路:
首先显然先用tarjan处理处各个强连通块。顺便处理处每个块中的点编号最大的是多少。以belong[x]表示x这个点属于哪个块。
对于一条边,若他不是桥边,则删去他后不会出现不联通的u,v,结果是0 0
如果是桥边,就以(belong[x], belong[y])为边加入新图中
然后我们缩完了点获得了一棵树.
若是桥边:太(wo)巧(tai)妙(chun)惹。
首先达成共识:若i这个块中,最大的点为maxn[i],则点maxn[i]+1和不在i这个块中。
同理若以maxn[i]表示i子树上的最大值,则点maxn[i]+1不在这i子树上
然后处理:
1.以n所在的块为根,处理这棵树。接下来都把块视为点了因为打字麻烦= =
2.处理出maxn[i]为i的子树中值得最大值 即 maxn[i] = max{maxn[j] | i 是 j 的祖先},这个可以简单树形DP处理出。顺便处理出每个点的深度。
3.接下来继续前面的枚举原图的每一条边。对于边两边的点:
(1)属于同一个块,答案如上面说的
(2)属于不同块。则设他们所在的块为u, v。可以知道在刚才那棵树中,u一定是v的父亲或者v一定是u的父亲(树上的边直接连接了他们),为了表达方便我们设u是父亲。删去边后变成了v子树以及除了v子树以外的一块。因为有maxn[v] <= maxn[u],且有maxn[v] + 1不在v子树上,所以maxn[v] +1一定在另一块上,也就是他们分开了,那么这是一组可行解。怎么证明是最大呢= =因为maxn[v]是v子树上最大值,没有别的值比他大了。。且不会选择出maxn[v] = n因为v一定是儿子,而n在整棵树的根上,他所在的块一定不是儿子。
大概说的挺清楚了吧,,啰嗦患者的日常= =
其他看代码吧,虽然注释也没多少= = 然后minn是没用的数组哈哈哈= =
1 #include <cstring> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 const int N = 100005; 6 7 int head[N], maxn[N], minn[N], dfn[N], low[N], st[N], belong[N]; 8 int deep[N]; 9 10 struct point{ 11 int u, v, next; 12 point(){}; 13 point(int x, int y, int z){ 14 u=x, v = y, next = z; 15 } 16 }p[N<<1]; 17 int no, top, num, id; 18 struct Ans{ 19 int x, y; 20 Ans(){}; 21 Ans(int _u, int _v){ 22 x = _u; y = _v; 23 } 24 }ans[N]; 25 void init(){ 26 memset(head, -1, sizeof(head)); 27 memset(dfn, -1, sizeof(dfn)); 28 memset(maxn, 0, sizeof(maxn)); 29 memset(minn, 0x3f, sizeof(minn)); 30 no = id = num = top = 0; 31 } 32 33 void add(int x, int y){ 34 p[no] = point(x, y, head[x]); 35 head[x] = no++; 36 p[no] = point(y, x, head[y]); head[y] =no++; 37 } 38 //tarjan求强连通,belong[i]表示属于哪个块 39 //记录每个块中编号最大值 40 void tarjan( int x, int fa){ 41 int cnt = 0; 42 low[x] = dfn[x] = ++num; 43 st[++top] = x; 44 int i, y; 45 for(i = head[x]; i != -1; i = p[i].next){ 46 y = p[i].v; 47 if(y == fa)continue; 48 if(dfn[y] == -1) { 49 tarjan(y, x); 50 low[x] = min(low[x], low[y]); 51 } 52 else low[x] = min(low[x], dfn[y]); 53 } 54 if(dfn[x] == low[x]){ 55 id ++; 56 do{ 57 y = st[top--]; 58 belong[y] = id; 59 maxn[id] = max(maxn[id], y); 60 minn[id] = min(minn[id], y); 61 }while(x != y); 62 } 63 } 64 //maxn表示i 及其子树的最大值 65 void dfs(int x, int fa){ 66 int i, y; 67 for(i = head[x]; i != -1; i = p[i].next){ 68 y = p[i].v; 69 if(y == fa) continue; 70 deep[y] = deep[x] + 1; 71 dfs(y, x); 72 maxn[x] = max(maxn[x], maxn[y]); 73 } 74 75 } 76 int main(){ 77 int TC, n, m, i, x, y, u, v; 78 scanf("%d", &TC); 79 while(TC--){ 80 scanf("%d%d", &n, &m); 81 init(); 82 for(i = 1; i <= m; i++){ 83 scanf("%d%d", &x, &y); 84 add(x, y); 85 } 86 tarjan(1, 1); 87 88 m = no; 89 no = 0; 90 memset(head, -1, sizeof(head)); 91 //重新建出树 92 for(i = 0; i < m; i+= 2){ 93 x = belong[p[i].u]; y = belong[p[i].v]; 94 //属于同一个块,非桥边,删了之后不出现不通的点 95 if(x == y) ans[i/2] = Ans(0, 0); 96 else { 97 ans[i/2] = Ans(x, y); 98 add(x, y); 99 } 100 } 101 deep[1] = 0; 102 //以n所在的块为根,dfs这棵树,求出子树的maxn 103 dfs(belong[n], 0); 104 m >>= 1; 105 for(i = 0; i < m; i++){ 106 if(ans[i].x == 0) printf("0 0\n"); 107 else{ 108 //因为只隔一条边 109 u = ans[i].x; v = ans[i].y; 110 if(deep[u] > deep[v]){ 111 printf("%d %d\n", maxn[u], maxn[u] + 1); 112 } else { 113 printf("%d %d\n", maxn[v], maxn[v] + 1); 114 } 115 } 116 } 117 } 118 return 0; 119 }