题目描述:
为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们已经厌倦了被迫走某一条路,所以她们想建一些新路,使每一对草场之间都会至少有两条相互分离的路径,这样她们就有多一些选择.
每对草场之间已经有至少一条路径.给出所有R(F-1≤R≤10000)条双向路的描述,每条路连接了两个不同的草场,请计算最少的新建道路的数量, 路径由若干道路首尾相连而成.两条路径相互分离,是指两条路径没有一条重合的道路.但是,两条分离的路径上可以有一些相同的草场. 对于同一对草场之间,可能已经有两条不同的道路,你也可以在它们之间再建一条道路,作为另一条不同的道路.
简单说,就是加多少条边可以让所有点都在环上(不一定是一个环)
题解:
边双联通:删掉一条边,图仍旧联通。于是乎:
这不就是题面的意思吗?!
模板题???
于是思路非常明确:tarjan完之后硬跑即可。
怎么硬跑呢?把所有的边双给找出来,缩成一个联通块,然后把点之间的边转化为联通块之间的边,这样,只要统计度为1的联通快,答案就get了。
把所有已有的环给缩起来,然后再找多少入度为1的点,加边肯定是给他们两两相连,达到最优
为什么嘞?
首先,在度为大于1的点上加边,找到的环不是最大环,就导致原本可以一条边解决的事被分了多次解决,于是只对度为1的点,把他们两两相连,统计出的数量即为答案。
于是,整个题目就很明朗了:
技巧:
1、因为是无向图,每两个点就构成了一个环,所有有一个神一般的操作:^1
2^1=3,3^1=2;4^1=5,5^1=4....
因为两个正反边是在一起存的,所以搜到一个边,然后用异或操作,就可以搞到下一条边得下标了,把它们都打上vis标记,就可以跑有向图了。
坑点:
1、因为有异或操作,所以1的话异或起来会是0,于是就跑不到了,所有cnt要初始化为1.....
2、如果ans是奇数,在/2的过程中,可能会损失精度,所有+1/2......
3、没了.....
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=100005; struct edge { int to,next; }e[maxn<<1]; int n,m,ans; int head[maxn<<1],cnt=1; int x[maxn],y[maxn]; inline void addedge(int from,int to) { e[++cnt].next=head[from]; e[cnt].to=to; head[from]=cnt; } int dfn[maxn],low[maxn],tot,vis[maxn],top; int st[maxn],color[maxn],col; int ru[maxn]; void tarjan(int u) { dfn[u]=low[u]=++tot; st[++top]=u; for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(!vis[i]) { vis[i]=vis[i^1]=1; if(dfn[v]==0) { tarjan(v); low[u]=min(low[u],low[v]); } else { low[u]=min(low[u],dfn[v]); } } } if(dfn[u]==low[u]) { color[u]=++col; while(st[top]!=u) color[st[top]]=col,top--; top--; } } int main() { scanf("%d%d",&n,&m);//n=read();m=read(); for(int i=1;i<=m;i++) { scanf("%d%d",&x[i],&y[i]);//x[i]=read();y[i]=read(); addedge(x[i],y[i]); addedge(y[i],x[i]); } for(int i=1;i<=n;i++) { if(!dfn[i]) tarjan(i); } for(int i=1;i<=m;i++) if(color[x[i]]!=color[y[i]]) ru[color[x[i]]]++,ru[color[y[i]]]++; for(int i=1;i<=col;i++) if(ru[i]==1) ans++; printf("%d ",ans+1>>1); return 0; }
( zrxdl %%%)
(完)