P2860()

题目描述:

为了从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\n",ans+1>>1);
    return 0;
}

 

( zrxdl %%%)

(完)

posted @ 2019-08-02 16:00  阿基米德的澡盆  阅读(101)  评论(0编辑  收藏  举报