首页 > 其他 > 详细

[NOI2014]魔法森林(LCT,最小生成树)

时间:2020-02-10 09:40:12      阅读:68      评论:0      收藏:0      [点我收藏+]

[NOI2014]魔法森林(luogu)

Description

 

题目描述

 

为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,

节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m。初始时小 E 同学在 1 号节点,隐士则住在 n号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。

魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 1号节点住着两种守护精灵:

A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。

只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边 e_ii包含两个权值 a_i与 b_i 。

若身上携带的 A 型守护精灵个数不少于 a_i? ,且 B 型守护精灵个数不少于 b_i ,这条边上的妖怪就不会对通过这条边的人发起攻击。

当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。

由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。

守护精灵的总个数为 A 型守护精灵的个数与 B 型守护精灵的个数之和。

 

输入格式

 

输入文件的第 1行包含两个整数 n,m,表示无向图共有 n 个节点,m 条边。 接下来 m 行,第 i+1 行包含 4 个正整数 X_i,Y_i,a_i,b_i

描述第 i 条无向边。 其中 X_i与 Y_i为该边两个端点的标号,a_i? 与 b_i? 的含义如题所述。 注意数据中可能包含重边与自环。

 

输出格式

 

输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携 带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出 -1

Solution

发现这道题可以用最小生成树的思想

将所有边按a从小到大排序,再按最小生成树加入图中,则此时1~n的最大a必定最小

出现环时找到环中b最大的一条边删去

那么我们需要一种可以动态加边删边和查询路径中最大b的数据结构,那就是LCT了

注意要把边和两端点都看成点,才能方便地删除和连边

Code

 

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N=15e4+10;
int fa[N],top[N],val[N],ch[N][2],rev[N],sum[N];
int n,m,ans=2e9;
struct node
{
    int x,y,a,b;
    bool operator <(const node &o)const
    {
        return a<o.a;
    }
}d[N];
inline char get()
{
    static char buf[1024];
    static int pos=0,size=0;
    if(pos==size)
    {
        size=fread(buf,1,1024,stdin);
        pos=0;
        if(!size) return EOF;
        else return buf[pos++];
    }
    else return buf[pos++];     
}
int read()
{
    int sum=0,fh=1;
    char ch=get();
    while(!(ch>=0 && ch<=9))
    {
        if(ch==-) fh=-1;
        ch=get();
    }
    while(ch>=0 && ch<=9 && ch!=EOF) sum=sum*10+ch-48,ch=get();
    return sum*fh;
}
void push_up(int x)
{
    sum[x]=x;
    if(ch[x][0] && val[sum[ch[x][0]]]>val[sum[x]]) sum[x]=sum[ch[x][0]];
    if(ch[x][1] && val[sum[ch[x][1]]]>val[sum[x]]) sum[x]=sum[ch[x][1]];
}
bool nroot(int x)
{
    return x==ch[fa[x]][0] || x==ch[fa[x]][1];
}
int get(int x)
{
    return x==ch[fa[x]][1];
}
void change(int x)
{
    if(!x) return ;
    swap(ch[x][0],ch[x][1]);
    rev[x]^=1;
}
void push_down(int x)
{
    if(rev[x])
    {
        change(ch[x][0]);
        change(ch[x][1]);
        rev[x]=0;
    }
}
void dfs(int x)
{
    if(nroot(x)) dfs(fa[x]);
    push_down(x);
}
void rotate(int x)
{
    int y=fa[x],z=fa[y],wh=get(x);
    if(nroot(y)) ch[z][get(y)]=x;
    if(ch[x][wh^1]) fa[ch[x][wh^1]]=y;
    ch[y][wh]=ch[x][wh^1];
    ch[x][wh^1]=y;
    fa[y]=x,fa[x]=z;
    push_up(y),push_up(x);
}
void splay(int x)
{
    dfs(x);
    for(int fx;fx=fa[x],nroot(x);rotate(x))
        if(nroot(fx)) rotate(get(x)==get(fx)?fx:x);
}
void access(int x)
{
    for(int y=0;x;x=fa[y=x])
        splay(x),ch[x][1]=y,push_up(x);
}
void makeroot(int x)
{
    access(x),splay(x);
    change(x);
}
int findroot(int x)
{
    access(x),splay(x);
    while(ch[x][0]) push_down(x),x=ch[x][0];
    splay(x);
    return x;
}
void link(int x,int y)
{
    makeroot(x);
    if(findroot(y)!=x) fa[x]=y;
}
void cut(int x,int y)
{
    makeroot(x);
    if(findroot(y)==x && fa[y]==x && !ch[y][0])
        fa[y]=ch[x][1]=0;
}
int split(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    return sum[y];
}
int get_top(int x)
{
    return x==top[x]?x:top[x]=get_top(top[x]);
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
        d[i]=(node){read(),read(),read(),read()};
    sort(d+1,d+1+m);
    for(int i=1;i<=n+m;i++) top[i]=sum[i]=i;
    for(int i=1;i<=m;i++) val[i+n]=d[i].b;
    for(int i=1;i<=m;i++)
    {
        int fx=get_top(d[i].x),fy=get_top(d[i].y);
        bool flag=true;
        if(fx==fy)
        {
            int w=split(d[i].x,d[i].y);
            if(val[w]>d[i].b) cut(d[w-n].x,w),cut(w,d[w-n].y);
            else flag=false;
        }
        else top[fx]=fy;
        if(flag) link(d[i].x,i+n),link(i+n,d[i].y);
        if(get_top(1)==get_top(n))
            ans=min(ans,d[i].a+val[split(1,n)]);
    }
    if(ans<2e9) printf("%d\n",ans);
    else puts("-1");
    return 0;
}

 

 

 

 

[NOI2014]魔法森林(LCT,最小生成树)

原文:https://www.cnblogs.com/hsez-cyx/p/12289762.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!