【结论】【非严格次小生成树】NKOJ3855 merlin

NKOJ3855 merlin
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 2s

问题描述
古月族是一个神奇的种族,有一天古月腾和古月豪在一起看《梅林传奇(merlin)》。
剧中,以卡梅洛特(camelot)为代表的是古时英格兰的城池坐落在现我大英帝国的某处土地上,每个城池被乌瑟王(亚瑟王之父)编号,方便货物来往。
假设有n座城池,编号为1-n,一些城池之间有单向道连接(逆向行驶会被视为私自入境),共k条。如果有一些城池能够互相到达交通无碍,他们会结为盟友,于是大英帝国上出现了许多帮派,他们要么互相挑起战争,要么选择井水不犯河水,形势极其尴尬,甚至,他们把连到其他帮派外的单向边拆毁了。
现在,乌瑟王死后,亚瑟王想要统一大英,他决定让魔法师梅林在一些城池之间修一些双向边,这样能使所有城池互相联系,交流,促进和平发展。梅林为了帮助亚瑟王,他把可能会修的道路都进行了魔法加特,使走上这条路的人心平气和,向往和平。虽然梅林魔法强大,但他也需要休息,他只能在指定的城池之间建立m条双向边,并且每条双向边上的有一个魔力需求值wi。
梅林已经累趴下了,谁叫亚瑟王这么折磨他(可怜的梅子),他想知道;
怎样连边,才能让所有城池能够互相到达,并且他耗费的魔力最少,求出这个最小值。
是否有不同种建边的方法。

输入格式
多组测试数据。
第一行一个正整数T,对于每一组测试数据:
第一行,三个个整数n,k,m。
接下来k行,每行两个整数,x,y,表示从x城池到y城池有一条单向道路。
接下来m行,每行三个整数,x,y,z,表示可以在城池x和城池y之间,建立一条双向道路,消耗魔法值为z。

输出格式
对于每一组测试数据:
第一行,一个整数表示消耗最少魔力。
第二行,若有不同的建边方法,输出“YES”,否则输出“NO”(均不含引号)。

样例输入 1
1
5 5 5
1 2
2 3
3 1
3 4
4 5
1 2 10
4 5 9
5 1 3
3 4 6
2 4 1

样例输出 1
4
NO

样例输入 2
1
5 5 5
1 2
2 3
3 4
4 5
5 1
1 2 100
2 3 123
4 3 1
1 2 1
2 3 1

样例输出 2
0
NO

样例输入 3
1
5 6 3
1 2
2 1
1 3
3 4
4 5
5 3
1 3 5
2 4 5
3 5 999

样例输出 3
5
YES

提示
【数据范围】
对于30%的数据, 1<=n<=1000,1<=T<=5。
对于100%的数据,1<=n<=500000,1<=m<=100000,1<=k<=1000000 ,1<=x<=n,1<=y<=n,0<=z<=10000,1<=T<=5。
保证m条双向边能够使所有城池交流畅通无阻。
注:有兴趣的同学可以思考,如果每个帮派不把连接到外帮派的单向边拆毁,应该怎么做。(本次考试请根据题目描述做题)。

来源 ht hjh

第一问:强连通分量缩点-> 最小生成树
第二问:

法一:依次加边、减掉环中除加入边外最大边(lca倍增讨论)->生成树大小增加否

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int needn=500003;
const int needm=100003;
const int needk=1000003;
const double lg2=log(2);

int n,k,m,mm;
//...........................................................
inline void in_(int & d)
{
    char t=getchar();
    while(t<'0'||t>'9') t=getchar();
    for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
}
//...........................................................
int tot,fi[needn],la[needk],en[needk],len[needk];
bool vis[needn];

void add(int a,int b,int c=0)
{
    tot++;
    la[tot]=fi[a];
    fi[a]=tot;
    en[tot]=b;
    len[tot]=c;
}
//...........................................................
int visittime,dfn[needn],low[needn],scc,id[needn];
int top,ss[needn];
bool ins[needn];

void tarjan(int x)
{
    low[x]=dfn[x]=++visittime;
    ss[++top]=x,ins[x]=true;
    for(int t=fi[x],y;t;t=la[t])
    {
        y=en[t];
        if(!dfn[y]) 
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y]) low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        int y;
        scc++;
        do{
            y=ss[top],top--;
            ins[y]=false;
            id[y]=scc;
        }while(y!=x);
    }
}
//...........................................................
struct fy
{
    int a,b,len;
    bool use;
    bool operator< (const fy& b) const
    {
        return len<b.len;
    }
};

fy w[needm];
//...........................................................
int be[needn];

int getbe(int x)
{
    return x==be[x] ?x :be[x]=getbe(be[x]);
}

void clean()
{
    for(int i=1;i<=scc;i++) be[i]=i;
}
//...........................................................
int ans1;
int dm;
int dep[needn],mdis[needn][22],fa[needn][22];

void build_lca(int x)
{
    vis[x]=true;
    int k=log(dep[x])/lg2;
    dm=max(dm,k);
    for(int i=1;i<=k;i++) 
     fa[x][i]=fa[fa[x][i-1]][i-1],mdis[x][i]=max(mdis[x][i-1],mdis[fa[x][i-1]][i-1]);
    for(int t=fi[x],y;t;t=la[t])
    {
        y=en[t];
        if(vis[y]) continue;
        dep[y]=dep[x]+1;
        fa[y][0]=x;
        mdis[y][0]=len[t];
        build_lca(y);
    }
}

bool lca(int k)
{
    int a=w[k].a,b=w[k].b;
    if(dep[a]>dep[b]) swap(a,b);
    int ans=0,c;
    for(int i=0,p=dep[b]-dep[a];i<=dm;i++) if((p>>i)&1) ans=max(ans,mdis[b][i]),b=fa[b][i];
    if(a==b) c=a;
    else 
    {
         for(int i=dm;i>=0;i--) 
         if(fa[a][i]!=fa[b][i])
         {
            ans=max(ans,mdis[a][i]),ans=max(ans,mdis[b][i]);
            b=fa[b][i],a=fa[a][i];
         }
         c=fa[a][0];
         ans=max(ans,mdis[a][0]),ans=max(ans,mdis[b][0]);
    }
    if(ans!=w[k].len) return false;
    return true;
}
//...........................................................
void ini()
{
    ans1=tot=0;
    scc=visittime=top=0;
    for(int i=1;i<=n;i++) vis[i]=fi[i]=dfn[i]=low[i]=id[i]=ins[n]=0;
    for(int i=1;i<=m;i++) w[i].use=false;
    for(int i=0,j;i<=dm;i++) for(j=1;j<=n;j++) mdis[j][i]=fa[j][i]=0;
}

//...........................................................
int main()
{
    int t;scanf("%d",&t);
while(t--)
{
    scanf("%d%d%d",&n,&k,&m);
    ini();
    for(int i=1,a,b;i<=k;i++)
    {
        in_(a),in_(b);
        add(a,b);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    mm=0;
    for(int i=1,a,b,c;i<=m;i++)
    {
        in_(a),in_(b),in_(c);
        if(id[a]==id[b]) continue;
        mm++;
        w[mm].a=id[a],w[mm].b=id[b],w[mm].len=c;
    }
    sort(w+1,w+1+mm);
    clean();
    for(int i=1;i<=scc;i++) fi[i]=0;
    for(int i=1,x,y,cnt=0;i<=mm&&cnt+1<scc;i++)
    {
        x=getbe(w[i].a),y=getbe(w[i].b);
        if(x!=y)
        {
            be[x]=y;
            ans1+=w[i].len;
            w[i].use=true;
            cnt++;
            add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
        }
    }
    build_lca(1);
    bool getans2=false;
    for(int i=1;i<=mm;i++)
    {
        if(!w[i].use&&lca(i)) 
        {
            getans2=true;
            break;
        }
    }
    printf("%d\n",ans1);
    puts(getans2 ? "YES":"NO");
}
}

法二:找出最小生成树后将所有边重新排序,使新的顺序为原来顺序中长度相等的元素反序,重新找最小生成树,如果新加入的边为原来未讨论过的,则YES。

struct fy
{
    int a,b,len,id;
    bool use;
};
bool way1 (const fy& x,const fy& y) 
{
    if(x.len==y.len) return x.id<y.id;
    return x.len<y.len;
}
bool way2 (const fy& x,const fy& y) 
{
    if(x.len==y.len) return x.id>y.id;
    return x.len<y.len;
}
........

    int ans1=0;
    sort(w+1,w+1+mm,way1);
    clean();
    for(int i=1,x,y,cnt=0;i<=mm&&cnt+1<scc;i++)
    {
        x=getbe(w[i].a),y=getbe(w[i].b);
        if(x!=y)
        {
            be[x]=y;
            ans1+=w[i].len;
            w[i].use=true;
            cnt++;
            add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
        }
    }

    bool getans2=false;
    sort(w+1,w+1+mm,way2);
    clean();
    for(int i=1,x,y,cnt=0;i<=mm&&cnt+1<scc;i++)
    {
        x=getbe(w[i].a),y=getbe(w[i].b);
        if(x!=y)
        {
            if(!w[i].use) 
            {
                getans2=true;
                break;
            }
            be[x]=y;
            w[i].use=true;
            cnt++;
            add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
        }
    }
    printf("%d\n",ans1);
    puts(getans2 ? "YES":"NO");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果您下载了本程序,但是该程序存在问题无法运行,那么您可以选择退款或者寻求我们的帮助(如果找我们帮助的话,是需要追加额外费用的)。另外,您不会使用资源的话(这种情况不支持退款),也可以找我们帮助(需要追加额外费用) 随着移动互联网技术的发展和用户需求的变化,【小程序名称】应运而生,以其轻量化、便捷化的设计理念为用户提供了一种全新的服务模式。作为一款无需下载安装即可使用的应用,【小程序名称】依托于微信庞大的生态系统,让用户在微信内就能轻松实现各种功能操作。 【小程序名称】的核心功能主要集中在【具体服务领域】,例如在线购物、本地生活服务、教育学习或健康管理等。它简化了传统APP繁琐的注册登录流程,支持微信一键授权登录,极大地提升了用户体验。用户通过搜索或扫描二维码,瞬间即可开启使用,享受快速加载、流畅运行的服务。 该小程序界面设计简洁明了,布局合理,易于上手。同时,其特色功能如实时更新的信息推送、个性化推荐以及社交分享功能,让用户能够及时获取所需信息,并方便地将优质内容分享至朋友圈或好友,实现信息的高效传播与互动。 【小程序名称】注重数据安全与隐私保护,严格遵守国家法律法规和微信平台的规定,确保用户数据的安全无虞。此外,其背后的开发团队持续迭代更新,根据用户反馈不断优化产品性能,提升服务质量,致力于打造一个贴近用户需求、充满活力的小程序生态。 总结来说,【小程序名称】凭借其小巧便携、快捷高效的特性,不仅节省了用户的手机存储空间,更为用户提供了无缝衔接的便利服务,是现代生活中不可或缺的一部分,真正实现了“触手可及”的智能生活新体验。只需轻点屏幕,无限精彩尽在掌握之中。
Merlin GPT是由OpenAI开发的一种自然语言处理(NLP)模型。它是GPT-3的一个变种,旨在生成更加质量高、多样性丰富的文本。 与传统的NLP模型不同,Merlin GPT采用了深度学习技术和大量的训练数据,以便更好地理解和生成人类语言。它使用了相当于1750亿个参数的深度神经网络,这使得它能够处理多种NLP任务,如文本生成、文章摘要、问题回答等。 Merlin GPT的设计目标是提供一种高效且易于使用的工具,以满足用户在自然语言处理方面的需求。它可以根据给定的输入生成连贯流畅、语法正确的文本,使得人机交互更加自然且高效。 在实际应用中,Merlin GPT可以用于各种任务,如机器翻译、文档自动摘要、自动问答、内容生成等。它可以在帮助写作、设计内容、进行创作等方面发挥重要作用。例如,可以利用Merlin GPT生成文章开头、写作建议,或者根据用户输入作出信息提供和解答问题。 尽管Merlin GPT在自然语言处理方面取得了很大的突破,但它仍然存在一些限制。例如,在生成文本时可能会出现语义模糊或错误的情况。此外,Merlin GPT也对敏感信息和不当内容缺乏辨别能力。 总而言之,Merlin GPT是一种先进的自然语言处理模型,具有强大的文本生成能力。它为用户提供了一种便捷且高效的方式来处理各种自然语言处理任务,为人机交互和创作提供了有力的支持。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值