标签:getch esc tar 设立 爸爸 缩点 algo put blog
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
HNOI蒯白书原题啊...
本题的模型是:在无向图上选择尽量少的点涂黑,使得任意删除一个点后,每个连通分量至少有一个黑点.
看到这种题首先就进行点双连通分量的缩点,然后这就是一棵树,且树上的节点是以割点为交集...
对于叶子节点(即只有一个割点),则该连通分量必须建一个,不然与爸爸的割点爆掉后就gi了
对于非叶子节点,就不用建,爆了和爸爸的还可以往儿子那里跑
特判如果没有割点的话,就需要任选两个建
点双连通分量的实现的话,先把割点找出来,然后不走割点即可(但割点是括在点双连通分量内的)
#include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=100050; int gi(){ int x=0,flag=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) flag=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); return x*flag; } int head[N],nxt[N],to[N],dfn[N],low[N],pd[N],tt,tong[N],cnt,tot,check[N],vis[N],vis2[N],size[N]; unsigned long long ans1,ans2; void tarjan(int x,int rt){ dfn[x]=low[x]=++tt;int flag=0; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(!dfn[y]){ tarjan(y,rt); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) flag++; } else low[x]=min(dfn[y],low[x]); } if(x!=rt&&flag) pd[x]=1; if(x==rt&&flag>1) pd[x]=1; } void dfs(int x,int frr){ vis[x]=1,size[frr]++; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(!vis[y]&&pd[y]==0) dfs(y,frr); else if(pd[y]==1&&vis2[y]!=frr) check[frr]++,vis2[y]=frr; } } void lnk(int x,int y){ to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt; to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt; } int main(){ int T=0; while(1){ int n,x,y;T++;n=gi(); if(n==0) return 0;int num=0; ans1=0,ans2=1,tt=0,tot=0,cnt=1; for(int i=1;i<=n;i++){ int x=gi(),y=gi(); if(!tong[x]) num++,tong[x]++; if(!tong[y]) num++,tong[y]++; lnk(x,y); } for(int i=1;i<=num;i++) if(!dfn[i]) tarjan(i,i); for(int i=1;i<=num;i++) if(!vis[i]&&pd[i]==0) dfs(i,++tot); for(int i=1;i<=tot;i++){ if(check[i]==0) ans1+=2,ans2*=(size[i]-1)*(size[i])/2; else if(check[i]==1) ans1++,ans2*=size[i]; } for(int i=1;i<=num;i++) head[i]=size[i]=check[i]=pd[i]=dfn[i]=low[i]=tong[i]=vis[i]=vis2[i]=0; cout<<"Case "<<T<<":"<<‘ ‘<<ans1<<‘ ‘<<ans2<<endl; } }
标签:getch esc tar 设立 爸爸 缩点 algo put blog
原文地址:http://www.cnblogs.com/qt666/p/6880504.html