【JZOJ A组】简单的操作

28 篇文章 0 订阅
11 篇文章 0 订阅

Description

从前有个包含n个点,m条边,无自环和重边的无向图。
对于两个没有直接连边的点u;v,你可以将它们合并。具体来说,你可以删除u;v及所有以它们作为端点的边,然后加入一个新点x,将它与所有在原图中与u或v有直接连边的点连边。
你需要判断是否能通过若干次合并操作使得原图成为一条链,如果能,你还需要求出这条链的最大长度

Input

从文件merge.in中读入数据。
第一行两个正整数n;m,表示图的点数和边数。
接下来m行,每行两个正整数u;v,表示u和v之间有一条无向边

Output

输出到文件merge.out中。
如果能通过若干次合并操作使得原图成为一条链,输出链的最大长度,否则输出-1

Sample Input

【样例1输入】
5 4
1 2
2 3
3 4
3 5
【样例2输入】
4 6
1 2
2 3
1 3
3 4
2 4
1 4

Sample Output

【样例1输出】
3
【样例2输出】
-1

Data Constraint

对于30%的数据, n<=10
对于70%的数据, m<=2000
对于100%的数据, n<=1000,m<=10^5

思路

首先考虑一个联通块
若这个联通块存在奇环,它就无法变成一条链。(画一下图就知道啦)
若这个图合法的话,就是一个二分图

先将二分图中的全部联通分量和每个点属于哪个联通分量求出来
这个可以用dfs实现
对于每一个连通分量都构造了一条链
而对于任意两条链
显然可以通过一 次合并操作将它们并成一条,长度为它们的长度之和
因此,答案就是所有连通块的直径之和
可以用spfa实现

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=100077,inf=0x3f3f3f3f;
int n,m,cnt,cntt,dis[maxn],d[maxn],co[maxn],list[maxn],f[maxn],ans;
bool bb,b[maxn];
struct E
{
    int to,next;
}e[maxn*2];
void add(int u,int v)
{
    e[++cnt].to=v; e[cnt].next=list[u]; list[u]=cnt;
}
void dfs(int u,int c,int t)
{
    if(bb) return;
    f[u]=t; co[u]=c;
    for(int i=list[u]; i; i=e[i].next)
    {
        if(bb) return;
        int v=e[i].to;
        if(!co[v])
        {
            dfs(v,-c,t);
            if(bb) return;
        }else
        if(co[u]+co[v])
        {
            bb=1; return;
        }
    }
}
int spfa(int s)
{
    for(int i=1; i<=n; i++) d[i]=inf;
    queue<int> q;
    q.push(s); d[s]=0; b[s]=1;
    int mx=0;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        mx=max(mx,d[u]);
        for(int i=list[u]; i; i=e[i].next)
        {
            int v=e[i].to;
            if(d[v]>d[u]+1)
            {
                d[v]=d[u]+1;
                if(!b[v]) q.push(v),b[v]=1;
            }
        }
        b[u]=0;
    }
    return mx;
}
int main()
{
    freopen("merge.in","r",stdin); freopen("merge.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    for(int i=1; i<=n; i++) if(!co[i])
    {
        dfs(i,1,++cntt);
        if(bb)
        {
            printf("-1"); return 0;
        }
    }
    for(int i=1; i<=n; i++) dis[f[i]]=max(dis[f[i]],spfa(i));
    for(int i=1; i<=cntt; i++) ans+=dis[i];
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值