JZOJ5806. 【2018.08.12提高A组模拟】 简单的操作

14 篇文章 0 订阅
6 篇文章 0 订阅

Description

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

题解

先判断无解的情况,
很显然一个三元环是无解的,
推广一下,只要有奇环就是无解。

考虑构造一种合法解,
枚举一个点作为链的顶端,
那么可以将所有到它距离相同的点缩成一个点。

这样最后出来的,一定是一条链,
如何是链最长,那显然是图的直径了。
对于不同的联通块,就将他们的答案加在一起。

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int n,m,nxt[N*2],lst[N],to[N*2],tot,x,y;
int ans,mx[N],cnt,col[N],dis[N],w[N];
int head,tail,q[N];
bool bz[N];

void ins(int x,int y)
{
    nxt[++tot]=lst[x];
    to[tot]=y;
    lst[x]=tot;
}

void dfs(int x,int fa,int dep)
{
    col[x]=cnt;w[x]=dep;
    for(int i=lst[x];i;i=nxt[i])
    {
        if(to[i]==fa)continue;
        if(!col[to[i]])dfs(to[i],x,dep^1);else
            if(w[x]==w[to[i]])ans=-1;
    }
}

void work(int st)
{
    memset(dis,128,sizeof(dis));
    memset(bz,1,sizeof(bz));
    for(dis[st]=head=bz[q[tail=1]=st]=0;head<tail;)
    {
        x=q[++head];
        for(int i=lst[x];i;i=nxt[i])
            if(dis[to[i]]<dis[x]+1 && bz[to[i]])
            {
                dis[to[i]]=dis[x]+1;
                bz[q[++tail]=to[i]]=0;
            }
    }
}

int main()
{
    freopen("merge.in","r",stdin);
    freopen("merge.out","w",stdout);

    read(n);read(m);
    for(int i=1;i<=m;i++)
        read(x),read(y),ins(x,y),ins(y,x);

    for(int i=1;i<=n;i++)
        if(!col[i])cnt++,dfs(i,0,0);

    if(ans==-1)
    {
        printf("%d",ans);
        return 0;
    }

    for(int i=1;i<=n;i++)
    {
        work(i);
        for(int i=1;i<=n;i++)
            mx[col[i]]=max(mx[col[i]],dis[i]);
    }

    for(int i=1;i<=cnt;i++)
        ans=ans+mx[i];

    printf("%d\n",ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值