2017.8.11 [HNOI2012]矿场搭建
【题目描述】
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
【输入格式】
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖煤点 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
【输出格式】
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
【样例输入】
9
1 3
4 1
3 5
1 2
2 6
1 5
6 3
1 6
3 2
6
1 2
1 3
2 4
2 5
3 6
3 7
0
【样例输出】
Case 1: 2 4
Case 2: 4 1
【提示】
Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。
【来源】
【题目来源】
solution
不看题解真是快做不出题了......
先tarjan求出所有的割点,割点会将整个连通图分成几部分
然后给这几部分染色,染到两遍以上就说明有两个其他的部分都可以到达这部分,一个矿塌陷无影响,所以只需要在其他两部分放救援出口
而只染过一遍的就必须放了
特别地:如果这个图是点双联通分量(即没有割点),就只需要放一个救援出口,方案是C(n,2)=n*(n-1)/2
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define ll long long 5 #define mem(a,b) memset(a,b,sizeof(a)) 6 using namespace std; 7 const int N=2506; 8 struct son 9 { 10 int v,next; 11 }; 12 son a1[N]; 13 int first[N],e; 14 void addbian(int u,int v) 15 { 16 a1[e].v=v; 17 a1[e].next=first[u]; 18 first[u]=e++; 19 } 20 21 int m,u,o,n,ans,cnt; 22 ll sum; 23 int fa[N],dfn[N],low[N],hh,cutsum; 24 int cut[N],num[N*3],color[N]; 25 bool vis[N],flag[N]; 26 27 void tarjen(int fa,int x) 28 { 29 int child=0; 30 low[x]=dfn[x]=++cnt; 31 for(int i=first[x];i!=-1;i=a1[i].next) 32 { 33 int temp=a1[i].v; 34 if(dfn[temp]==-1) 35 { 36 ++child; 37 tarjen(x,temp); 38 if(low[x]>low[temp])low[x]=low[temp]; 39 if(low[temp]>=dfn[x]) 40 cut[x]=1; 41 } 42 else 43 if(dfn[temp]<dfn[x]&&temp!=fa) 44 if(low[x]>dfn[temp]) 45 low[x]=dfn[temp]; 46 } 47 if(fa<0&&child==1) 48 cut[x]=0; 49 } 50 51 void out11() 52 { 53 printf("\n"); 54 for(int i=1;i<=n;++i) 55 printf("%d\n",cut[i]); 56 printf("\n"); 57 for(int i=1;i<=n;++i) 58 printf("%d\n",color[i]); 59 printf("\n"); 60 } 61 62 void chu() 63 { 64 mem(first,-1);mem(a1,0);e=0; 65 mem(dfn,-1);mem(low,0);cnt=0; 66 mem(cut,0);mem(num,0);mem(color,-1); 67 mem(flag,0); 68 ans=0;sum=1;cnt=0;hh=0; 69 cutsum=0; 70 n=0; 71 } 72 73 int now; 74 75 void dfs(int x) 76 { 77 vis[x]=1; 78 //printf("x=%d\n",x); 79 if(color[x]!=-1) 80 color[x]=-2; 81 else 82 color[x]=now; 83 for(int i=first[x];i!=-1;i=a1[i].next) 84 { 85 int temp=a1[i].v; 86 if(cut[temp]||vis[temp])continue; 87 dfs(temp); 88 } 89 } 90 91 int main(){ 92 93 //freopen("1.txt","r",stdin); 94 //freopen("bzoj_2730.in","r",stdin); 95 //freopen("bzoj_2730.out","w",stdout); 96 97 scanf("%d",&m); 98 for(int l=1;m!=0;++l) 99 { 100 chu(); 101 102 for(int i=1;i<=m;++i) 103 { 104 scanf("%d%d",&u,&o); 105 addbian(u,o); 106 addbian(o,u); 107 n=n>u?n:u; 108 n=n>o?n:o; 109 } 110 111 for(int i=1;i<=n;++i) 112 if(dfn[i]==-1) 113 tarjen(-1,i); 114 115 116 for(int i=1;i<=n;++i) 117 if(cut[i]) 118 { 119 mem(vis,0); 120 for(int j=first[i];j!=-1;j=a1[j].next) 121 { 122 int temp=a1[j].v; 123 if(cut[temp]||vis[temp])continue; 124 now=++hh; 125 //printf("temp=%d\n",temp); 126 dfs(temp); 127 } 128 } 129 130 for(int i=1;i<=n;++i) 131 { 132 if(cut[i])++cutsum; 133 if(vis[i]==-1||cut[i])continue; 134 ++num[color[i]]; 135 } 136 137 for(int i=1;i<=hh;++i) 138 if(num[i]) 139 { 140 ++ans; 141 sum*=num[i]; 142 } 143 144 //out11(); 145 146 if(cutsum==0)//点双连通分量 147 { 148 ans=2; 149 sum=(n-1)*n/2; 150 } 151 152 //printf("cutsum=%d\n",cutsum); 153 154 printf("Case %d: %d %lld\n",l,ans,sum); 155 scanf("%d",&m); 156 } 157 //while(1); 158 return 0; 159 }#include<cstdio> 160 #include<cstring> 161 #include<iostream> 162 #define ll long long 163 #define mem(a,b) memset(a,b,sizeof(a)) 164 using namespace std; 165 const int N=2506; 166 struct son 167 { 168 int v,next; 169 }; 170 son a1[N]; 171 int first[N],e; 172 void addbian(int u,int v) 173 { 174 a1[e].v=v; 175 a1[e].next=first[u]; 176 first[u]=e++; 177 } 178 179 int m,u,o,n,ans,cnt; 180 ll sum; 181 int fa[N],dfn[N],low[N],hh,cutsum; 182 int cut[N],num[N*3],color[N]; 183 bool vis[N],flag[N]; 184 185 void tarjen(int fa,int x) 186 { 187 int child=0; 188 low[x]=dfn[x]=++cnt; 189 for(int i=first[x];i!=-1;i=a1[i].next) 190 { 191 int temp=a1[i].v; 192 if(dfn[temp]==-1) 193 { 194 ++child; 195 tarjen(x,temp); 196 if(low[x]>low[temp])low[x]=low[temp]; 197 if(low[temp]>=dfn[x]) 198 cut[x]=1; 199 } 200 else 201 if(dfn[temp]<dfn[x]&&temp!=fa) 202 if(low[x]>dfn[temp]) 203 low[x]=dfn[temp]; 204 } 205 if(fa<0&&child==1) 206 cut[x]=0; 207 } 208 209 void out11() 210 { 211 printf("\n"); 212 for(int i=1;i<=n;++i) 213 printf("%d\n",cut[i]); 214 printf("\n"); 215 for(int i=1;i<=n;++i) 216 printf("%d\n",color[i]); 217 printf("\n"); 218 } 219 220 void chu() 221 { 222 mem(first,-1);mem(a1,0);e=0; 223 mem(dfn,-1);mem(low,0);cnt=0; 224 mem(cut,0);mem(num,0);mem(color,-1); 225 mem(flag,0); 226 ans=0;sum=1;cnt=0;hh=0; 227 cutsum=0; 228 n=0; 229 } 230 231 int now; 232 233 void dfs(int x) 234 { 235 vis[x]=1; 236 //printf("x=%d\n",x); 237 if(color[x]!=-1) 238 color[x]=-2; 239 else 240 color[x]=now; 241 for(int i=first[x];i!=-1;i=a1[i].next) 242 { 243 int temp=a1[i].v; 244 if(cut[temp]||vis[temp])continue; 245 dfs(temp); 246 } 247 } 248 249 int main(){ 250 251 //freopen("1.txt","r",stdin); 252 //freopen("bzoj_2730.in","r",stdin); 253 //freopen("bzoj_2730.out","w",stdout); 254 255 scanf("%d",&m); 256 for(int l=1;m!=0;++l) 257 { 258 chu(); 259 260 for(int i=1;i<=m;++i) 261 { 262 scanf("%d%d",&u,&o); 263 addbian(u,o); 264 addbian(o,u); 265 n=n>u?n:u; 266 n=n>o?n:o; 267 } 268 269 for(int i=1;i<=n;++i) 270 if(dfn[i]==-1) 271 tarjen(-1,i); 272 273 274 for(int i=1;i<=n;++i) 275 if(cut[i]) 276 { 277 mem(vis,0); 278 for(int j=first[i];j!=-1;j=a1[j].next) 279 { 280 int temp=a1[j].v; 281 if(cut[temp]||vis[temp])continue; 282 now=++hh; 283 //printf("temp=%d\n",temp); 284 dfs(temp); 285 } 286 } 287 288 for(int i=1;i<=n;++i) 289 { 290 if(cut[i])++cutsum; 291 if(vis[i]==-1||cut[i])continue; 292 ++num[color[i]]; 293 } 294 295 for(int i=1;i<=hh;++i) 296 if(num[i]) 297 { 298 ++ans; 299 sum*=num[i]; 300 } 301 302 //out11(); 303 304 if(cutsum==0)//点双连通分量 305 { 306 ans=2; 307 sum=(n-1)*n/2; 308 } 309 310 //printf("cutsum=%d\n",cutsum); 311 312 printf("Case %d: %d %lld\n",l,ans,sum); 313 scanf("%d",&m); 314 } 315 //while(1); 316 return 0; 317 }