简单题

题目描述
dzy 手上有一张n 个点m 条边的联通无向图,仙人掌是一张每条边最多在一个简单环内的联通无向图。他想求这个无向图的生成仙人掌中最多有多少条边。
但是dzy 觉得这个问题太简单了,于是他定义了“美丽的生成仙人掌”,即在一个生成仙人掌中如果满足对于任意编号为 ij(i<j) 的两点,存在一条它们之间的简单路径上面有 ji+1 个点,则这个仙人掌是美丽的。
他现在想要知道这张图的美丽的生成仙人掌中最多有多少条边,你能帮帮他吗?
输入
第一行两个整数n,m。接下来m 行每行两个整数ui,vi,表示这两个点之间有一条无向边。保证图中没有自环。
输出
仅一行一个整数表示答案。
样例输入
2 1
1 2
样例输出
1
提示
【数据规模和约定】

对于10% 的数据,n <=10。

对于30% 的数据,n <=10^3。

对于100% 的数据,n <=10^5,m <= 2n。

第一次接触仙人掌。。。一开始凭借以前的粗略了解,以为是数据结构。。。好吧事实证明我还是too young too simple.
首先,一个没有环的美丽仙人掌肯定是这样的:
12345...n (-表示有边相连)
这样才能满足ij间简单路径必须有j-i+1个点的条件。
然后因为每条边只能在一个环内,假设这条链的区间为 [l,r] ,那么仙人掌剩下的边肯定是 [l,r] 间互不交叉的边。(可以画图模拟一下,交叉的话就有两个环了)
这样问题就变成了,给你m条边,使它先形成数条链,然后在各条链中添加最多的互不交叉的边(这显然可以用贪心做)。
但是怎么找链呢?我们可以用并查集把同一条链上的点合并起来。
p[i]i
s[i]i

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,x,y,u,v,tot,ans;
struct ty
{
    int a,b;
}e[200005];
int f[100005],p[100005],s[100005];
int get(int x)
{
    if(f[x]==x) return x;else return f[x]=get(f[x]);
}
bool cmp(ty x,ty y)
{
    return x.b<y.b;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++) 
    {
        scanf("%d%d",&x,&y);
        if(x>y) swap(x,y);
        if(y-x!=1) 
        {
            tot++;
            e[tot].a=x;
            e[tot].b=y;
        }
        else
        {
            u=get(x);
            v=get(y);
            if(v!=u) //合并左右端点相邻的边
            {
                f[v]=u;
                s[u]=s[u]+s[v]+1;
                s[v]=0;
            }
        }
    }   
    if(tot>0) 
    {
        sort(e+1,e+tot+1,cmp);
        x=0;
        for(int i=1;i<=tot;i++) 
        {
            u=get(e[i].a);
            v=get(e[i].b);
            if(u==v&&e[i].a>=p[u]) //该边左右端点必须在同一集合内
            {
                s[u]++;
                p[u]=e[i].b;
            }
        }       
    }
    for(int i=1;i<=n;i++) ans=max(ans,s[i]);
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值