FloodFill
不说了….大家都会写…..
无向图割点
我们使用DFS对每个点记录两个值:
1) dfn[i] 表示点i是第几个DFS到的点.
2) low[i] 表示从点i出发,经过点i的某个子孙节点(包括它自己),然后通过非树边绕回而取得的最小的dfn值.
算法正确性依赖于如下定理:
定理: 一个点x是割点,当且仅当满足下列条件之一:
1) x是DFS搜索树的根,且x有多于1棵子树.
2) x不是DFS搜索树的根,且存在一个节点v,满足:
1. v是x在搜索树上的直接子节点;
2. 在v及其子树中,没有任何一条边后向边指向x的祖先;
注意无向图不存在横叉边(即从一颗子树连向另一棵子树,这两棵子树没有交集).
有了如上定理,使用dfn与low可将其转化为:
定理: 一个点x是割点,当且仅当满足下列条件之一:
1) x是DFS搜索树的根,且x拥有多于1棵子树.
2) x不是DFS搜索树的根,且存在一个节点v,满足:
1.v是x在搜索树上的直接子节点;
2.low[v] == dfn[x].
满足条件的v可能有多个,删掉节点x所分离的连通块个数为满足条件的v的个数+1.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio>
2 #include <iostream>
3 #include <fstream>
4
5 #include <cmath>
6 #include <cstdlib>
7 #include <algorithm>
8 #include <cstring>
9
10 typedef long long ll;
11 typedef unsigned long long ull;
12 typedef unsigned int uint;
13 typedef double db;
14
15 int getint()
16 {
17 int res=0; char c=getchar(); bool m=false;
18 while(c<'0' || c>'9') m=(c=='-'),c=getchar();
19 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
20 return m ? -res : res;
21 }
22
23 const db eps=1e-18;
24 bool feq(db a,db b)
25 { return fabs(b-a)<eps; }
26
27 using namespace std;
28
29 const int INF=(1<<30)-1;
30
31 struct edge
32 {
33 int in;
34 edge*nxt;
35 }pool[200000];
36 edge*et=pool;
37 edge*eds[1050];
38 inline edge*addedge(int i,int j)
39 { et->in=j; et->nxt=eds[i]; eds[i]=et++; }
40 #define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt)
41
42 int n,m;
43 bool used[1050];
44
45 int dfn[1050];
46 int low[1050];
47 int cnt[1050];
48 int f[1050];
49 bool ins[1050];
50 int cur;
51 void DFS(int x)
52 {
53 used[x]=true;
54 low[x]=dfn[x]=cur++;
55
56 ins[x]=true;
57
58 FOREACH_EDGE(i,x)
59 {
60 if(!used[i->in])
61 {
62 DFS(i->in);
63 if(low[i->in]==dfn[x]) cnt[x]++;
64 low[x]=min(low[x],low[i->in]);
65 }else
66 {
67 if(i->in!=f[x])
68 low[x]=min(low[x],dfn[i->in]);
69 }
70 }
71
72 ins[x]=false;
73 }
74
75 void INIT()
76 {
77 et=pool;
78 memset(eds,0,sizeof(eds));
79 memset(dfn,0,sizeof(dfn));
80 memset(low,0,sizeof(low));
81 memset(cnt,0,sizeof(cnt));
82 memset(f,0,sizeof(f));
83 memset(used,0,sizeof(used));
84 }
85
86 int main()
87 {
88 int a,b,T=1;
89 while(a=getint())
90 {
91 b=getint();
92 INIT();
93 addedge(a,b);
94 addedge(b,a);
95 while(a=getint())
96 {
97 b=getint();
98 addedge(a,b);
99 addedge(b,a);
100 }
101
102 cur=1;
103 f[1]=1;
104 DFS(1);
105
106 printf("Network #%d\n",T);
107
108 int tot=0;
109
110
111 if(cnt[1]>=2)
112 printf(" SPF node %d leaves %d subnets\n",1,cnt[1]),tot++;
113
114 for(int i=2;i<=1000;i++)
115 {
116 if(cnt[i]!=0)
117 {
118 printf(" SPF node %d leaves %d subnets\n",i,cnt[i]+1);
119 tot++;
120 }
121 }
122
123 if(!tot)
124 printf(" No SPF nodes\n");
125
126 printf("\n");
127
128 T++;
129 }
130 return 0;
131 }
强连通分量
dfn和low跟上边一样,没有什么区别.....
定理: 强连通分量在搜索树上表现为一棵子树.
使用那个栈的目的是去除一整棵子树.注意low值并不代表强连通分量的子树的根.下面是AC BZOJ 1051的代码.
一道裸的求强连通分量.(为什么大家都是一眼题TAT我根本没想到啊这个做法....)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
struct edge
{
int in;
edge*nxt;
}pool[205000];
edge*et=pool;
edge*eds[50050];
void addedge(int i,int j)
{ et->in=j; et->nxt=eds[i]; eds[i]=et++; }
#define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt)
int block[50050],btot;
int cnt;
int dfn[50050];
int low[50050];
int s[50050],st;
bool ins[50050];
bool used[50050];
void DFS(int x)
{
dfn[x]=low[x]=++cnt;
s[st++]=x;
used[x]=ins[x]=true;
FOREACH_EDGE(i,x)
{
if(!used[i->in])
{
DFS(i->in);
low[x]=min(low[x],low[i->in]);
}
else
if(ins[i->in])
low[x]=min(low[x],dfn[i->in]);
}
if(dfn[x]==low[x])
{
btot++;
int p;
do
{
p=s[--st];
ins[p]=false;
block[p]=btot;
}
while(p!=x);
}
}
int deg[50050];
int n,m;
int main()
{
n=getint();
m=getint();
for(int i=0;i<m;i++)
{
int a=getint()-1;
int b=getint()-1;
addedge(b,a);
}
for(int i=0;i<n;i++)
if(!used[i]) DFS(i);
for(int i=0;i<n;i++)
FOREACH_EDGE(e,i)
{
if(block[i]!=block[e->in]) //not belong to the same block.
deg[block[e->in]]++; //intake degree of block.
}
int resb=0;
for(int i=1;i<=btot;i++)
{
if(deg[i]==0)
{
if(resb==0)
resb=i;
else
{
printf("0\n");
return 0;
}
}
}
int res=0;
for(int i=0;i<n;i++)
if(block[i]==resb) res++;
printf("%d\n",res);
return 0;
}
具体的做法,每头牛设一个点.
牛A被其它所有牛认为是受欢迎的,等价于所有点都有到达点A的路径.
等价于,如果我们把原图的边反过来(u,v)->(v,u),那么从A出发必定能到达其它所有点.
我们把反过来的图先强连通分量缩点得到DAG(有向无环图). <--这里想不到TAT 是我做题少了么
那么,那些受欢迎的牛,一定在DAG"开头"的位置,即入度为0的点(强连通分量)中.(如果入度不为0,比如说边(u,v)使得v的入度不为0,所以v必定不能到达u(否则就有u->v->u的环,就不是DAG了))
于是,找到入度为0的强连通分量,特判一下是否唯一就好了(不唯一必无解,不能从一个入度为0的分量走到另一个入度为0的分量.).....
另外一题 BZOJ 1179 APIO2009 ATM
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <fstream> 3 #include <iostream> 4 5 #include <cstdlib> 6 #include <cstring> 7 #include <algorithm> 8 #include <cmath> 9 10 #include <queue> 11 #include <vector> 12 #include <map> 13 #include <set> 14 #include <stack> 15 #include <list> 16 17 typedef unsigned int uint; 18 typedef long long int ll; 19 typedef unsigned long long int ull; 20 typedef double db; 21 22 using namespace std; 23 24 inline int getint() 25 { 26 int res=0; 27 char c=getchar(); 28 bool mi=false; 29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar(); 30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar(); 31 return mi ? -res : res; 32 } 33 inline ll getll() 34 { 35 ll res=0; 36 char c=getchar(); 37 bool mi=false; 38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar(); 39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar(); 40 return mi ? -res : res; 41 } 42 43 db eps=1e-20; 44 inline bool feq(db a,db b) 45 { return fabs(a-b)<eps; } 46 47 template<typename Type> 48 inline Type avg(const Type a,const Type b) 49 { return a+((b-a)/2); } 50 51 //========================================================== 52 //========================================================== 53 //========================================================== 54 //========================================================== 55 56 57 struct edge 58 { 59 int in; 60 edge*nxt; 61 }pool[4005000]; 62 edge*et=pool; 63 edge*eds[1005000]; 64 edge*edp[1005000]; 65 void addedge(int i,int j) 66 { et->in=j; et->nxt=eds[i]; eds[i]=et++; } 67 #define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt) 68 69 int v[1005000]; 70 bool endp[1005000]; 71 72 int block[505000]; int btot=0; 73 bool used[1005000]; 74 int low[505000]; 75 int dfn[505000]; int cnt=0; 76 int ins[505000]; 77 int stk[505000]; int sp=0; 78 void DFS(int x) 79 { 80 dfn[x]=low[x]=++cnt; 81 used[x]=true; 82 ins[x]=true; 83 stk[sp++]=x; 84 FOREACH_EDGE(i,x) 85 { 86 if(!used[i->in]) 87 { 88 DFS(i->in); 89 low[x]=min(low[x],low[i->in]); 90 } 91 else 92 if(ins[i->in]) 93 low[x]=min(low[x],dfn[i->in]); 94 } 95 96 if(dfn[x]==low[x]) 97 { 98 int p; 99 do 100 { 101 p=stk[--sp]; 102 block[p]=btot; 103 ins[p]=false; 104 } 105 while(p!=x); 106 btot++; 107 } 108 } 109 110 int n,m; 111 int st; 112 113 typedef pair<int,bool> pl; 114 pl d[1005000]; 115 116 pl RSDFS(int x) 117 { 118 if(used[x]) return d[x]; 119 120 int res=0; 121 bool ok=false; 122 used[x]=true; 123 124 FOREACH_EDGE(i,x) 125 { 126 pl s=RSDFS(i->in); 127 if(s.second) 128 { 129 ok=true; 130 res=max(res,s.first); 131 } 132 } 133 134 return d[x]=pl(res+v[x],ok||endp[x]); 135 } 136 137 int main() 138 { 139 n=getint(); 140 m=getint(); 141 for(int i=0;i<m;i++) 142 { 143 int a=getint()-1; 144 int b=getint()-1; 145 addedge(a,b); 146 } 147 for(int i=0;i<n;i++) 148 v[i]=getint(); 149 st=getint()-1; 150 151 int h=getint(); 152 for(int i=0;i<h;i++) 153 endp[getint()-1]=true; 154 155 btot=n; 156 for(int i=0;i<n;i++) 157 if(!used[i]) DFS(i); 158 159 for(int i=0;i<n;i++) 160 { 161 v[block[i]]+=v[i]; 162 endp[block[i]]|=endp[i]; 163 } 164 st=block[st]; 165 166 for(int i=0;i<n;i++) 167 FOREACH_EDGE(e,i) 168 if(block[i]!=block[e->in]) 169 addedge(block[i],block[e->in]); 170 171 printf("%d\n",RSDFS(st).first); 172 173 return 0; 174 }
做法是,缩点变成DAG,然后给每个DAG上的节点打上标记,DAG上节点的权值就是原图在这个分量里面的点的权值和,然后就可以DAG-DP了.....记录两个值,一个是从这个节点出发能拿到多少钱,另一个是从这个节点出发能不能走到终止节点(酒吧).
转图真是坑爹啊.....
还好BZOJ没卡栈,否则又要写一遍toposort......TAT
无向图割边&双连通分量
边拆点大法好.
为每条边设一个点x,原图中(u,v)变成(u,x)和(x,v)两条无向边.
然后跑割点就行了o(╯□╰)...
下面是代码 AC VIJOS 1325
题意是求割边条数,以及使得原图双连通需要添加的最少边数.
后面一问,考虑双连通分量缩点后原图变成一棵树,每条树边都是割边,每条边最多能构建1个环(连接2个叶节点),所以答案是叶节点个数除以2上取整.
给出的图是连通图.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio>
2 #include <fstream>
3 #include <iostream>
4
5 #include <cstdlib>
6 #include <cstring>
7 #include <algorithm>
8 #include <cmath>
9
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21
22 using namespace std;
23
24 inline int getint()
25 {
26 int res=0;
27 char c=getchar();
28 bool mi=false;
29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
31 return mi ? -res : res;
32 }
33
34 //==============================================================================
35 //==============================================================================
36 //==============================================================================
37 //==============================================================================
38
39
40 struct edge
41 {
42 int in;
43 edge*ptr;
44 edge*nxt;
45 }pool[100050];
46 edge*et=pool;
47 edge*eds[20050];
48 void addedge(int a,int b)
49 {
50 et->ptr=et+1;
51 et->in=b; et->nxt=eds[a]; eds[a]=et++;
52 et->ptr=et-1;
53 et->in=a; et->nxt=eds[b]; eds[b]=et++;
54 }
55 #define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt)
56
57 int n,m,q;
58
59 int dfn[20050];
60 int low[20050];
61 bool used[20050];
62 int curd;
63 int cut[20050];
64
65 void DFS(int x)
66 {
67 dfn[x]=low[x]=++curd;
68 used[x]=true;
69
70 FOREACH_EDGE(i,x)
71 {
72 if(!used[i->in])
73 {
74 DFS(i->in);
75 low[x]=min(low[x],low[i->in]);
76 }
77 else
78 low[x]=min(low[x],dfn[i->in]);
79
80 if(low[i->in]==dfn[x])
81 cut[x]=true;
82 }
83 }
84
85 int block[20050];
86 int btot;
87 void FFA(int x)
88 {
89 if(cut[x] && x>=n) return ; //not go across a cut edge.
90 block[x]=btot;
91 used[x]=true;
92 FOREACH_EDGE(i,x)
93 if(!used[i->in])
94 FFA(i->in);
95 }
96
97 int deg[20050];
98
99 int main()
100 {
101 n=getint();
102 m=getint();
103
104 for(int i=0;i<m;i++)
105 {
106 int a,b;
107 a=getint()-1;
108 b=getint()-1;
109 addedge(a,n+i);
110 addedge(b,n+i);
111 }
112
113 DFS(0);
114
115 //Color Blocks.
116 memset(used,0,sizeof(bool)*(n+m+1));
117 for(int i=0;i<n;i++)
118 if(!used[i]) { btot++; FFA(i); }
119
120 //Count degree.
121 for(int i=n;i<n+m;i++)
122 if(cut[i]) FOREACH_EDGE(e,i)
123 deg[block[e->in]]++;
124
125 int lcnt=0;
126 //Count leaves.
127 for(int i=1;i<=btot;i++)
128 if(deg[i]==1) lcnt++;
129
130 int ccnt=0;
131 //Count cuts.
132 for(int i=n;i<n+m;i++)
133 if(cut[i]) ccnt++;
134
135 printf("%d\n%d\n",ccnt,(lcnt+1)/2);
136
137 return 0;
138 }
割边还有一种无需对图进行转换的算法.
AC POJ 3352 其实跟VIJOS1325是一个样的...
WA了两发....一是多组数据(题目写了吗?!)....二是根节点的割点判定要特别处理.....
一条边(u,v)是割边,当且仅当满足一下条件之一:
1) u与v其中一个点的度为1.
2) u与v均为割点.
哎不对啊.......考虑一个三角形,选两个顶点另接两个点......
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <fstream> 3 #include <iostream> 4 5 #include <cstdlib> 6 #include <cstring> 7 #include <algorithm> 8 #include <cmath> 9 10 #include <queue> 11 #include <vector> 12 #include <map> 13 #include <set> 14 #include <stack> 15 #include <list> 16 17 typedef unsigned int uint; 18 typedef long long int ll; 19 typedef unsigned long long int ull; 20 typedef double db; 21 22 using namespace std; 23 24 inline int getint() 25 { 26 int res=0; 27 char c=getchar(); 28 bool mi=false; 29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar(); 30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar(); 31 return mi ? -res : res; 32 } 33 inline ll getll() 34 { 35 ll res=0; 36 char c=getchar(); 37 bool mi=false; 38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar(); 39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar(); 40 return mi ? -res : res; 41 } 42 43 db eps=1e-32; 44 inline bool feq(db a,db b) 45 { return fabs(a-b)<eps; } 46 47 template<typename Type> 48 inline Type avg(const Type a,const Type b) 49 { return a+((b-a)/2); } 50 51 //=================================================================== 52 //=================================================================== 53 //=================================================================== 54 //=================================================================== 55 56 57 struct edge 58 { 59 int in; 60 edge*nxt; 61 bool cut; 62 }pool[50050]; 63 edge*et; 64 edge*eds[10050]; 65 void addedge(int a,int b) 66 { 67 et->in=b; et->nxt=eds[a]; eds[a]=et++; 68 et->in=a; et->nxt=eds[b]; eds[b]=et++; 69 } 70 #define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt) 71 72 73 int n,m; 74 75 int low[10050]; 76 bool used[10050]; 77 int dfn[10050]; 78 bool iscut[10050]; 79 int dc=0; 80 void DFS(int x) 81 { 82 dfn[x]=low[x]=++dc; 83 used[x]=true; 84 85 int tot=0; 86 FOREACH_EDGE(i,x) 87 { 88 if(!used[i->in]) 89 { 90 DFS(i->in); 91 low[x]=min(low[x],low[i->in]); 92 } 93 else 94 low[x]=min(low[x],dfn[i->in]); 95 96 if(low[i->in]==dfn[x]) iscut[x]=true; 97 98 tot++; 99 } 100 101 if(x==0) if(tot>1) iscut[x]=true; else iscut[x]=false; 102 } 103 104 int deg[10050]; 105 106 int block[10050]; 107 int btot; 108 int cnt[10050]; 109 110 void FFA(int x) 111 { 112 if(block[x]) return ; 113 block[x]=btot; 114 FOREACH_EDGE(i,x) 115 if(!i->cut) FFA(i->in); 116 } 117 118 119 int main() 120 { 121 while(scanf("%d%d",&n,&m)>0) 122 { 123 et=pool; 124 memset(eds,0,sizeof(int)*(n+1)); 125 memset(low,0,sizeof(int)*(n+1)); 126 memset(dfn,0,sizeof(int)*(n+1)); 127 memset(used,0,sizeof(bool)*(n+1)); 128 memset(deg,0,sizeof(int)*(n+1)); 129 memset(block,0,sizeof(int)*(n+1)); 130 memset(cnt,0,sizeof(int)*(n+1)); 131 dc=btot=0; 132 133 for(int i=0;i<m;i++) 134 { 135 int a,b; 136 a=getint()-1; 137 b=getint()-1; 138 addedge(a,b); 139 deg[a]++; 140 deg[b]++; 141 } 142 143 DFS(0); 144 145 for(int i=0;i<n;i++) 146 FOREACH_EDGE(e,i) 147 if( deg[e->in]==1 || deg[i]==1 || (iscut[e->in] && iscut[i]) ) 148 e->cut=true; 149 150 memset(used,0,sizeof(bool)*(n+1)); 151 for(int i=0;i<n;i++) 152 if(block[i]==0) { ++btot; FFA(i); } 153 154 for(int i=0;i<n;i++) 155 FOREACH_EDGE(e,i) 156 if(e->cut) cnt[block[i]]++; 157 158 int tot=0; 159 for(int i=1;i<=btot;i++) 160 if(cnt[i]==1) tot++; 161 162 printf("%d\n",(tot+1)/2); 163 } 164 165 return 0; 166 }
连通分量嘛,就是把所有割边标记好,然后跑一边FloodFill染色就好啦o(╯□╰)o
还是边拆点大法好
AC VIJOS 1769
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <fstream> 3 #include <iostream> 4 5 #include <cstdlib> 6 #include <cstring> 7 #include <algorithm> 8 #include <cmath> 9 10 #include <queue> 11 #include <vector> 12 #include <map> 13 #include <set> 14 #include <stack> 15 #include <list> 16 17 typedef unsigned int uint; 18 typedef long long int ll; 19 typedef unsigned long long int ull; 20 typedef double db; 21 22 using namespace std; 23 24 inline int getint() 25 { 26 int res=0; 27 char c=getchar(); 28 bool mi=false; 29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar(); 30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar(); 31 return mi ? -res : res; 32 } 33 inline ll getll() 34 { 35 ll res=0; 36 char c=getchar(); 37 bool mi=false; 38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar(); 39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar(); 40 return mi ? -res : res; 41 } 42 43 db eps=1e-80; 44 inline bool feq(db a,db b) 45 { return fabs(a-b)<eps; } 46 47 template<typename Type> 48 inline Type avg(const Type a,const Type b) 49 { return a+((b-a)/2); } 50 51 //=================================================================== 52 //=================================================================== 53 //=================================================================== 54 //=================================================================== 55 56 57 struct edge 58 { 59 int in; 60 edge*nxt; 61 }pool[1205000]; 62 edge*et=pool; 63 edge*eds[405000]; 64 void addedge(int a,int b) 65 { 66 et->in=b; et->nxt=eds[a]; eds[a]=et++; 67 et->in=a; et->nxt=eds[b]; eds[b]=et++; 68 } 69 #define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt) 70 71 72 int n,m,ac,bc,ntot; 73 int ast,bst; 74 75 76 bool iscut[2][405000]; 77 78 int low[405000]; 79 int dfn[405000],cnt=0; 80 bool used[405000]; 81 82 int ign,lbp; 83 void DFS(int x) 84 { 85 dfn[x]=low[x]=++cnt; 86 used[x]=true; 87 88 FOREACH_EDGE(i,x) 89 if(i->in!=ign) 90 { 91 if(!used[i->in]) 92 { 93 DFS(i->in); 94 low[x]=min(low[x],low[i->in]); 95 } 96 else 97 low[x]=min(low[x],dfn[i->in]); 98 99 if(low[i->in]==dfn[x]) 100 iscut[lbp][x]=true; 101 } 102 } 103 104 //blocks define 105 #define EDGENODE(i) ((i)+n+2) 106 107 int ablock[205000]; 108 int bblock[205000]; 109 110 int main() 111 { 112 n=getint(); 113 m=getint(); 114 ac=getint(); 115 bc=getint(); 116 ntot=n+m+2; 117 118 ast=n; 119 bst=n+1; 120 121 for(int i=0;i<ac;i++) ablock[i]=getint()-1; 122 for(int i=0;i<bc;i++) bblock[i]=getint()-1; 123 124 for(int i=0;i<m;i++) 125 { 126 int a,b; 127 a=getint()-1; 128 b=getint()-1; 129 addedge(a,EDGENODE(i)); 130 addedge(b,EDGENODE(i)); 131 } 132 133 134 //deal with A. 135 ign=bst; lbp=0; 136 for(int i=0;i<ac;i++) 137 addedge(ast,ablock[i]); 138 DFS(ast); 139 140 memset(low,0,sizeof(int)*(ntot+1)); 141 memset(dfn,0,sizeof(int)*(ntot+1)); 142 memset(used,0,sizeof(bool)*(ntot+1)); 143 cnt=0; 144 145 ign=ast; lbp=1; 146 for(int i=0;i<bc;i++) 147 addedge(bst,bblock[i]); 148 DFS(bst); 149 150 int cnt=0; 151 for(int i=0;i<m;i++) 152 if(iscut[0][EDGENODE(i)] || iscut[1][EDGENODE(i)]) 153 cnt++; 154 155 printf("%d\n",cnt); 156 157 for(int i=0;i<m;i++) 158 if(iscut[0][EDGENODE(i)] || iscut[1][EDGENODE(i)]) 159 printf("%d\n",i+1); 160 161 162 return 0; 163 }
关于无向图的搜索树和dfs序
如何快速判断一个无向图中,删掉一个点后,它的邻点的连通情况?
求出DFS序以后,在搜索树上做判断: 假设两个邻点为a,b.删掉的点为c.
那么a在c引领的子树内的充要条件为 dfn(c)<=dfn(a) 并且 dfn(a)<dfn(c)+SizeofSubTree(c).
用这个判断: 1.a,b分立在c的两个子树内. 那么a,b连通的条件是 low[a]<=dfn(c) 并且 low[b]<=dfn(c).
2.c在a到b的路径上. a,b连通的条件是 low[a]<=dfn(c). (假设a在c的子树内,b不在.注意无向图没有横叉边.)
其它所有情况一定都是连通的.