A: 交通运输线
时间限制: 5 Sec 内存限制: 128 MB
题目描述
战后有很多城市被严重破坏,我们需要重建城市。然而,有些建设材料只能在某些地方产生。因此,我们必须通过城市交通,来运送这些材料的城市。由于大部分道路已经在战争期间完全遭到破坏,可能有两个城市之间没有道路。当然在运输线中,更不可能存在圈。
现在,你的任务来了。给你战后的道路情况,我们想知道,两个城市之间是否存在道路,如果存在,输出这两个城市之间的最短路径长度。
输入
第一行一个整数Case(Case<=10)表示测试数据组数。
每组测试数据第一行三个整数N,M和C (2<=N<=10, 000) (0<=M<10, 000) (1<=c<=1000000)共有N个城市,现存M条边,共有C对运输需求。
接下来M行,每行三个整数A和B,D(1<=A,B<=N,A不等于B)表示从A到B有一条直接的公路,且距离为D。
最后C行,每行两个整数,S和T(1<=S,T<=N,S不等于T),即询问从S到T的最短路径长度。
输出
共Case行,否存在从S到T的路径,则输出最短路径,否则输出“Not connected”。
样例输入
1
5 3 2
1 3 2
2 4 3
5 2 3
1 4
4 5
样例输出
Not connected
6
这题总不能直接跑个最短路吧,
肯定有什么条件没用到,给你的图还是个森林。
显然的,两个点在不同树上就不联通,那在同一个树上的最短路呢?
画几个图你会发现两个点间只有一条简单路径,不就是最短路了吗?
证明的话,用反证法如果存在另一条简单路径,图就不是树了。
好了,先在我们就可以用LCA求出这个最短路径。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 #define N 2000005 8 queue<int> Q; 9 int dis[N],v[N],col[N],fa[N],anc[N],ans[N]; 10 struct node{ 11 int v,c; 12 }; 13 vector<node> vec[N]; 14 vector<node> vec_[N]; 15 int find(int x){ 16 return x==fa[x]?x:fa[x]=find(fa[x]); 17 } 18 void Dis(int u,int f){ 19 v[u]=1; 20 for (int size=vec[u].size(),i=0;i<size;i++) 21 if (vec[u][i].v!=f){ 22 dis[vec[u][i].v]=dis[u]+vec[u][i].c; 23 Dis(vec[u][i].v,u); 24 } 25 } 26 void LCA(int u,int f){ 27 Q.push(u); v[u]=1; fa[u]=u; anc[u]=u; 28 for (int size=vec[u].size(),i=0;i<size;i++) 29 if (vec[u][i].v!=f){ 30 int v_=vec[u][i].v; 31 LCA(v_,u); 32 int f_a=find(v_),f_b=find(u); 33 fa[f_a]=f_b; 34 anc[f_b]=u; 35 } 36 col[u]=1; 37 for (int size=vec_[u].size(),i=0;i<size;i++) 38 if (col[vec_[u][i].v]){ 39 ans[vec_[u][i].c]=dis[u]+dis[vec_[u][i].v]-dis[anc[find(vec_[u][i].v)]]-dis[anc[find(vec_[u][i].v)]]; 40 } 41 } 42 int main(){ 43 int Case; scanf("%d",&Case); 44 while (Case--){ 45 int n,m,c; scanf("%d%d%d",&n,&m,&c); 46 for (int i=1;i<=n;i++) { 47 vec[i].clear(); 48 dis[i]=0; 49 } 50 for (int i=1;i<=c;i++) ans[i]=-1,vec_[i].clear(); 51 for (int i=1;i<=m;i++){ 52 int a,b,c; scanf("%d%d%d",&a,&b,&c); 53 vec[a].push_back({b,c}); 54 vec[b].push_back({a,c}); 55 } 56 for (int i=1;i<=c;i++){ 57 int a,b; scanf("%d%d",&a,&b); 58 vec_[a].push_back({b,i}); 59 vec_[b].push_back({a,i}); 60 } 61 for (int i=1;i<=n;i++) v[i]=0; 62 for (int i=1;i<=n;i++) 63 if (!v[i]){ 64 Dis(i,0); 65 } 66 for (int i=1;i<=n;i++) v[i]=0; 67 for (int i=1;i<=n;i++) 68 if (!v[i]) { 69 while (!Q.empty()) col[Q.front()]=0,Q.pop(); 70 LCA(i,0); 71 } 72 for (int i=1;i<=c;i++) 73 if (ans[i]==-1) printf("Not connected\n"); 74 else printf("%d\n",ans[i]); 75 } 76 return 0; 77 }
B:这不是Floyd
时间限制: 1 Sec 内存限制: 128 MB
题目描述
当一个有向图给出,我们可以通过著名的Floyd算法方便的求解出其传递闭包。
但如果你给一个图G=(V,E),您能不能给一个的最小的边集合代替原边集,使得新的图与原图各个顶点的传递性不变。
输入
有多组测试数据:
第一行,包含一个整数Num,表示测试数据的个数。(1<=Num<=100)
每组测试数据,第一行一个整数N(1<=N<=200)。
接下来N行N列的一个0,1矩阵,表示相应点对之间的连接关系。第i行第j列,若值为1,则表示有一条从i到j的有向边。否则就没有。
输出
每行输出一个整数。输出至少需要多少条边,就能与原图的传递性一致。
样例输入
4
1
1
2
1 0
0 1
2
1 1
1 1
3
1 1 1
0 1 1
0 0 1
样例输出
0
0
2
2
简单的想法,一个强连通图里里只要个数条边(一个点不算),
然后我们可以尝试去缩点得到一个DAG,
然后就是玄学了,如果一条边去掉后对图的联通性没有影响就可以去掉。
感觉能用拓扑图证一下。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 31 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 32 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 33 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 34 if(last==‘-‘)ans=-ans; return ans; 35 } 36 #define N 80005 37 #define mem(a) memset(a,0,sizeof(a)) 38 int v[N],next[N],head[N],Stack[N],Ins[N],dfn[N],low[N],belong[N],map[205][205],map_[205][205],E,e,Time,top,sum[N],ans,len,l[N],r[N]; 39 void add(int x,int y){ v[++e]=y; next[e]=head[x]; head[x]=e; } 40 void dfs(int u){ 41 dfn[u]=low[u]=++Time; Stack[++top]=u; Ins[u]=1; 42 for (int i=head[u];i;i=next[i]) 43 if (dfn[v[i]]==0){ 44 dfs(v[i]); 45 low[u]=min(low[u],low[v[i]]); 46 } else if (Ins[v[i]]) low[u]=min(low[u],dfn[v[i]]); 47 if (dfn[u]==low[u]){ 48 E++; 49 while (Stack[top]!=u){ 50 belong[Stack[top]]=E; sum[E]++; 51 Ins[Stack[top]]=0; top--; 52 } 53 belong[Stack[top]]=E; sum[E]++; 54 Ins[Stack[top]]=0; top--; 55 if (sum[E]>1) ans+=sum[E]; 56 } 57 } 58 int main() 59 { 60 int T=read(); 61 while (T--){ 62 int n=read(); ans=E=e=Time=top=len=0; 63 mem(v),mem(next),mem(head),mem(Stack),mem(Ins); 64 mem(dfn),mem(low),mem(belong),mem(map),mem(map_),mem(sum),mem(l),mem(r); 65 for (int i=1;i<=n;i++) 66 for (int j=1;j<=n;j++){ 67 map[i][j]=read(); 68 if (map[i][j]) add(i,j); 69 } 70 for (int i=1;i<=n;i++) 71 if (dfn[i]==0) dfs(i); 72 for (int i=1;i<=n;i++) 73 for (int j=1;j<=n;j++) 74 if (map[i][j] && belong[i]!=belong[j]){ 75 len++; l[len]=belong[i]; r[len]=belong[j]; map_[belong[i]][belong[j]]++; 76 } 77 ans+=len; 78 for(int k=1;k<=E;k++) 79 for(int i=1;i<=E;i++) 80 for(int j=1;j<=E;j++) 81 map_[i][j]=map_[i][j]+map_[i][k]*map_[k][j]; 82 for(int i=1;i<=len;i++) 83 { 84 map_[l[i]][r[i]]--; 85 if(map_[l[i]][r[i]]==0) map_[l[i]][r[i]]++; 86 else ans--; 87 } 88 printf("%d\n",ans); 89 } 90 return 0; 91 }
C: 图中的项链
时间限制: 1 Sec 内存限制: 128 MB
题目描述
一个无向图中的项链,是由一系列圈组成,C1,C2,……,Ck(K>=1),并且符合如下规定:
1. 任何两个圈都没有公共边;
2. 任意两个相邻的圈有且仅有一个公共点;
3. 任意两个不相邻的圈没有公共点。
4. 任意一个点,至多只能出现在一个圈中一次。
现在要求一个从S到T的项链,C1,C2,……,Ck(K>=1),其中S属于C1,T属于Ck。
给定一个无向图,以及两个点S和T,问是否存在从S到T的项链。
输入
第一行一个整数Case(Case<=10)表示测试数据组数。
每组测试数据第一行两个整数N,M N (2<=N<=10, 000) (1<=M<=100, 000)分别表示无向图中点的数目和边的数目。
接下来M行,每行两个整数A和B(1<=A B<=N,A不等于B)表示有一条无向边从A到B。
最后两个整数S和T(1<=S T<=N,S不等于T)
输出
共Case行,输出”YES”或”No”,表示是否存在从S到T的项链。
样例输入
3
3 3
1 2
2 3
3 1
1 3
4 5
1 2
2 3
1 3
3 4
3 4
1 4
4 5
1 2
1 2
2 3
3 4
3 4
1 4
样例输出
YES
YES
NO
这种题给的条件太多了,肯定有没用的,或可以放宽的。
3,4完全是屁话。不用鸟它,2可以合并到1上。
1不就是双联通分量吗?
WOC模板题。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 //void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 31 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 32 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 33 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 34 if(last==‘-‘)ans=-ans; return ans; 35 } 36 #define N 200005 37 #define mem(a) memset(a,0,sizeof(a)) 38 int s,t,v[N],next[N],head[N],E[N],vis[N],e,e_,dfn[N],low[N],Time,Stack[N],top,p[N]; 39 void add(int x,int y,int e_){ v[++e]=y; next[e]=head[x]; head[x]=e; E[e]=e_;} 40 void dfs(int u){ 41 vis[u]=1; dfn[u]=low[u]=++Time; Stack[++top]=u; 42 for (int i=head[u];i;i=next[i]) 43 if(!vis[v[i]]) 44 { 45 p[v[i]]=E[i]; 46 dfs(v[i]); 47 low[u]=min(low[u],low[v[i]]); 48 } 49 else if(p[u]!=E[i]) 50 low[u]=min(low[u],dfn[v[i]]); 51 if (low[u]==dfn[u]){ 52 if (u==s){ 53 for (int i=1;i<=top;i++) 54 if (Stack[i]==t) { 55 cout<<"YES"<<endl; 56 return; 57 } 58 cout<<"NO"<<endl; return; 59 } else { 60 while (top>=1 && Stack[top]!=u) top--; 61 top--; 62 } 63 } 64 } 65 int main() 66 { 67 int T=read(); 68 while (T--){ 69 int n=read(),m=read(); 70 mem(head),mem(vis),mem(v),mem(low),mem(dfn),mem(next),mem(E),mem(Stack),mem(p); 71 e=e_=Time=top=0; 72 for (int i=1;i<=m;i++){ 73 int a=read(),b=read(); 74 add(a,b,++e_); add(b,a,e_); 75 } 76 s=read(),t=read(); 77 dfs(s); 78 } 79 return 0; 80 }
D: 桐桐的糖果计划
时间限制: 1 Sec 内存限制: 128 MB
题目描述
桐桐很喜欢吃棒棒糖。他家处在一大堆糖果店的附近。 但是,他们家的区域经常出现塞车、塞人等情况,这导致他不得不等到塞的车或人走光了他才能去买到他最爱吃的棒棒糖品种。于是,他去找市长帮他修路,使得每两个糖果店之间至少有两条完全不同的路。可是市长经费有限,于是让桐桐找出哪些路被塞住后会使某些糖果店与糖果店间无法到达及最少的修路条数。你能帮助他吃到他最喜爱的糖果吗? 注:1-> 3-> 2 和 1-> 3-> 4-> 2 为不完全不同的路,即不符合题意的路。 1-> 3-> 4-> 2 和 1-> 5-> 2 为完全不同的路,即符合题意的路。
输入
输入第一行是两个数n,m(n< =5000,m< =10000) 接下来的m行,每行两个数i,j,表示i,j间有一条边连接。
输出
输出有两行。第一行为塞住后就不可以到达某些糖果店的道路条数,第二行为最少的修路条数。
样例输入
7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7
样例输出
3
2
又一道模板题飘过,求桥,和缩点后树上入度为1的(个数+1)/2。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 31 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 32 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 33 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 34 if(last==‘-‘)ans=-ans; return ans; 35 } 36 #define N 20005 37 int v[N],next[N],head[N],E[N],cut[N],dfn[N],low[N],Stack[N],belong[N],Ins[N],top,e,e_,Time,p[N],num,nu[N],ans,ans_; 38 void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; E[e]=z; } 39 void dfs(int u){ 40 dfn[u]=low[u]=++Time; Stack[++top]=u; Ins[u]=1; 41 for (int i=head[u];i;i=next[i]) 42 if (dfn[v[i]]==0){ 43 p[v[i]]=E[i]; 44 dfs(v[i]); 45 low[u]=min(low[u],low[v[i]]); 46 if (low[v[i]]>dfn[u]) 47 cut[i]=1,ans_++; 48 } else if (E[i]!=p[u]) low[u]=min(low[u],dfn[v[i]]); 49 if (low[u]==dfn[u]){ 50 num++; 51 while (Stack[top]!=u) { 52 Ins[Stack[top]]=0; 53 belong[Stack[top]]=num; 54 top--; 55 } 56 Ins[Stack[top]]=0; 57 belong[Stack[top]]=num; 58 top--; 59 } 60 } 61 int main() 62 { 63 int n=read(),m=read(); 64 for (int i=1;i<=m;i++){ 65 int a=read(),b=read(); 66 add(a,b,++e_); add(b,a,e_); 67 } 68 for (int i=1;i<=n;i++) 69 if (dfn[i]==0) dfs(i); 70 for (int i=1;i<=n;i++){ 71 for (int j=head[i];j;j=next[j]) 72 if (cut[j]){ 73 nu[belong[i]]++; nu[belong[v[j]]]++; 74 } 75 } 76 for (int i=1;i<=num;i++) 77 if (nu[i]==1) ans++; 78 printf("%d\n",ans_); 79 printf("%d",(ans+1)/2); 80 return 0; 81 }
E: [USACO 4.2.2]完美的牛栏
时间限制: 1 Sec 内存限制: 64 MB
题目描述
农夫约翰上个星期刚刚建好了他的新牛棚,他使用了最新的挤奶技术。不幸的是,由于工程问题,每个牛栏都不一样。第一个星期,农夫约翰随便地让奶牛们进入牛栏,但是问题很快地显露出来:每头奶牛都只愿意在她们喜欢的那些牛栏中产奶。上个星期,农夫约翰刚刚收集到了奶牛们的爱好的信息(每头奶牛喜欢在哪些牛栏产奶)。一个牛栏只能容纳一头奶牛,当然,一头奶牛只能在一个牛栏中产奶。 给出奶牛们的爱好的信息,计算最大分配方案。
输入
第一行 两个整数,N (0 <= N <= 200) 和 M (0 <= M <= 200) 。N 是农夫约翰的奶牛数量,M 是新牛棚的牛栏数量。 第二行到第N+1行 一共 N 行,每行对应一只奶牛。第一个数字 (Si) 是这头奶牛愿意在其中产奶的牛栏的数目 (0 <= Si <= M) 。后面的 Si 个数表示这些牛栏的编号。牛栏的编号限定在区间 (1..M) 中,在同一行,一个牛栏不会被列出两次。
输出
只有一行。输出一个整数,表示最多能分配到的牛栏的数量。
样例输入
5 5
2 2 5
3 2 3 4
2 1 5
3 1 2 5
1 2
样例输出
4
二分图最大匹配
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=‘ ‘,ch=getchar(); 33 while(ch<‘0‘ || ch>‘9‘)last=ch,ch=getchar(); 34 while(ch>=‘0‘ && ch<=‘9‘)ans=ans*10+ch-‘0‘,ch=getchar(); 35 if(last==‘-‘)ans=-ans; return ans; 36 } 37 int n,m,map[300][300],v[300],match[300],ans; 38 bool dfs(int x){ 39 int t; 40 for (int i=1;i<=m;i++) 41 if (!v[i] && map[x][i]){ 42 v[i]=1; 43 t=match[i]; 44 match[i]=x; 45 if (t==-1||dfs(t)) 46 return 1; 47 match[i]=t; 48 } 49 return 0; 50 } 51 int main() 52 { 53 n=read(),m=read(); 54 memset(match,255,sizeof(match)); 55 for (int i=1;i<=n;i++){ 56 int t=read(); 57 for (int j=1;j<=t;j++) { 58 int a=read(); 59 map[i][a]=1; 60 } 61 } 62 for (int i=1;i<=n;i++){ 63 memset(v,0,sizeof(v)); 64 if (dfs(i)) ans++; 65 } 66 cout<<ans<<endl; 67 return 0; 68 }