题目: hdu3671
http://acm.hdu.edu.cn/showproblem.php?pid=3671
题意:给一个无向图,要求毁掉两个点,使图变得不连通,图一开始是连通的
因为要毁掉两个点,就不是简单的求割点,再看看数据范围,点数为1000,边数为10000,Tarjan的时间复杂度为O(E),如果用枚举法,先枚举要毁掉的第一个点,再用Tarjan进行处理来找割点会不会超时呢?答案是不会,时间为O(v*E),刚好是千万级别,不超
做法:先枚举要删除的第1个点,在原图中删除它,看看删除它后整个图的变化
1.整个图变得不连通了(即这个点本身是割点),但是还没完要分类讨论一下
(1).整个图变为两部分,但是两部分刚好都是一个点,那么这两个点再毁掉哪个点都好,图的连通分支数都不
会增加,这是一个特殊情况
例如,(1,2)(2,3)这种图,是无解的,任意毁掉两个点都无法增加图的连通分支,所以方案数为0
(2).整个图分为两部分,但是有一部分的点数为1,另一部分大于1,那么这时候只要在较大的那部分,任意毁掉一个点(无论是不是割点都行),最后整个图都会至少被分为了两个部分
(如果毁掉的是割点,将分成更多份),所以这样产生的方案数是V-2
(3).整个图分为了两个部分,两个部分的点数都大于1,那么任意在哪个部分毁掉那个点都可以(无论是不是割点都行),最后整个图都会至少分为两个部分,所以方案数为V-1
(4).整个图被分为了三个或更多的部分,那么也是在剩下的点中任意毁掉一个点都可以(无论那个点是不是割点),方案数为V-1
(如果这个点刚好处于一个部分且这个部分只有它自己一个点,那么 毁掉后整个图的分支数减1;如果这个点在一个部分且这个部分不止它一个点且这个点不是割点,那么分支数 不会增加,如果是割点分支数为增加)
2.删除第一个点后,整个图还是连通的(是连通,不是双连通)
那么就在剩下的图中找割点,找到几个,方案数就是多少
最后注意一点,这样计算的结果,很容易想到是有重复的,但是不难想到,其实刚好重复了一次,因为对于一个图,方案是固定的,枚举了所有点,找出了所有已方案,相当于每个方案算了两次,最后答案除2即可
1 #include<iostream> 2 #include<cstring> 3 //#include<bits/stdc++.h> 4 #include<math.h> 5 #include<algorithm> 6 #include<queue> 7 #include<stack> 8 #include<cstdio> 9 #include<map> 10 #include<set> 11 #define si(a) scanf("%d",&a) 12 #define sl(a) scanf("%lld",&a) 13 #define sii(a,b) scanf("%d%d",&a,&b) 14 #define sll(a,b) scanf("%lld%lld",&a,&b) 15 #define queues priority_queue 16 #define mod 1000000007 17 #define mem(a) memset(a,0,sizeof(a)); 18 #define def(a) ((a)&(-a)) 19 #define mp make_pair 20 #define pb push_back 21 #define fi first 22 #define se second 23 typedef long long ll; 24 const int INF=0x3f3f3f3f; 25 using namespace std; 26 const int MAX=1003; 27 vector<int>q[MAX]; 28 int DFN[MAX]; 29 int VIM[MAX]; 30 int top; 31 int clore; 32 int num; 33 set<int>s; 34 void tarjan(int a,int fa,int d) 35 { 36 int child=0; 37 DFN[a]=VIM[a]=++top; 38 num++; 39 for(int i:q[a]) 40 { 41 if(i==fa||i==d)continue; 42 if(!DFN[i]) 43 { 44 child++; 45 tarjan(i,a,d); 46 VIM[a]=min(VIM[a],VIM[i]); 47 if(VIM[i]>=DFN[a]&&a!=fa) 48 s.insert(a); 49 } 50 else VIM[a]=min(VIM[a],DFN[i]); 51 } 52 if(a==fa&&child>=2)s.insert(a); 53 } 54 int main() 55 { 56 int n,m; 57 int z=0; 58 while(cin>>n>>m&&n+m) 59 { 60 int sum=0; 61 top=0; 62 for(int i=1; i<=n; i++) 63 q[i].clear(); 64 for(int i=1; i<=m; i++) 65 { 66 int a,b; 67 cin>>a>>b; 68 q[a].push_back(b); 69 q[b].push_back(a); 70 } 71 for(int i=1; i<=n; i++) 72 { 73 int count1=0; 74 int ok=0; 75 clore=0; 76 s.clear(); 77 mem(VIM); 78 mem(DFN); 79 for(int j=1; j<=n; j++) 80 if(j!=i&&!DFN[j]) 81 { 82 count1++; 83 num=0; 84 tarjan(j,j,i); 85 if(num==1)ok++; 86 } 87 if(count1>=3)sum+=n-1; 88 else if(count1==2&&ok==1)sum+=n-2; 89 else if(count1==2&&ok==0)sum+=n-1; 90 else if(count1==1)sum+=s.size(); 91 } 92 printf("Case %d: %d ",++z,sum/2); 93 } 94 95 }