码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj3669: [Noi2014]魔法森林 lct

时间:2018-05-31 20:34:29      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:生成树   first   ota   syn   protector   联通   struct   noi   rev   

为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。
题解:lct维护最小生成树,先按a排序,然后挨个加边当有有回路时删掉最大的那个b边,然后当1和n联通时,每次都要算一下a和最大的b的和,就相当于把a遍历加边,然后保证联通的情况下找最小的b,
lct维护连通性,把边权转化为点权,每个边变成一个点,向两端各连一条边,然后加边和删边都很方便了

/**************************************************************
    Problem: 3669
    User: walfy
    Language: C++
    Result: Accepted
    Time:6608 ms
    Memory:9136 kb
****************************************************************/
 
//#pragma comment(linker, "/stack:200000000")
//#pragma GCC optimize("Ofast,no-stack-protector")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
//#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define pi acos(-1.0)
#define ll long long
#define vi vector<int>
#define mod 1000000007
#define ld long double
#define C 0.5772156649
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define pil pair<int,ll>
#define pli pair<ll,int>
#define pii pair<int,int>
#define cd complex<double>
#define ull unsigned long long
#define base 1000000000000000000
#define fio ios::sync_with_stdio(false);cin.tie(0)
 
using namespace std;
 
const double eps=1e-6;
const int N=200000+10,maxn=100000+10,inf=0x3f3f3f3f,INF=0x3f3f3f3f3f3f3f3f;
 
struct LCT{
    int fa[N],ch[N][2],rev[N],ma[N],val[N],id[N],q[N];
    inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    inline void pushup(int x)
    {
        id[x]=x;ma[x]=val[x];
        if(ma[x]<ma[ch[x][0]])
        {
            ma[x]=ma[ch[x][0]];
            id[x]=id[ch[x][0]];
        }
        if(ma[x]<ma[ch[x][1]])
        {
            ma[x]=ma[ch[x][1]];
            id[x]=id[ch[x][1]];
        }
    }
    inline void pushdown(int x)
    {
        if(rev[x])
        {
            rev[x]=0;swap(ch[x][0],ch[x][1]);
            rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
        }
    }
    inline void Rotate(int x)
    {
        int y=fa[x],z=fa[y],l,r;
        if(ch[y][0]==x)l=0,r=l^1;
        else l=1,r=l^1;
        if(!isroot(y))
        {
            if(ch[z][0]==y)ch[z][0]=x;
            else ch[z][1]=x;
        }
        fa[x]=z;fa[y]=x;fa[ch[x][r]]=y;
        ch[y][l]=ch[x][r];ch[x][r]=y;
        pushup(y);pushup(x);
    }
    inline void splay(int x)
    {
        int top=1;q[top]=x;
        for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
        for(int i=top;i;i--)pushdown(q[i]);
        while(!isroot(x))
        {
            int y=fa[x],z=fa[y];
            if(!isroot(y))
            {
                if((ch[y][0]==x)^(ch[z][0]==y))Rotate(x);
                else Rotate(y);
            }
            Rotate(x);
        }
    }
    inline void access(int x){for(int y=0;x;y=x,x=fa[x])splay(x),ch[x][1]=y,pushup(x);}
    inline void makeroot(int x){access(x),splay(x),rev[x]^=1;}
    inline int findroot(int x){access(x),splay(x);while(ch[x][0])x=ch[x][0];return x;}
    inline void split(int x,int y){makeroot(x),access(y),splay(y);}
    inline void cut(int x,int y){split(x,y);if(ch[y][0]==x)ch[y][0]=0,fa[x]=0;}
    inline void link(int x,int y){makeroot(x),fa[x]=y,splay(x);}
}lct;
struct edge{
    int u,v,a,b;
    bool operator <(const edge &rhs)const{
        return a<rhs.a || (a==rhs.a && b<rhs.b);
    }
}e[maxn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b);
    sort(e+1,e+1+m);
    for(int i=1;i<=m;i++)
    {
        lct.val[i+n]=lct.ma[i+n]=e[i].b;
        lct.id[i+n]=i+n;
    }
    int ans=inf;
    for(int i=1;i<=m;i++)
    {
        int fx=lct.findroot(e[i].u),fy=lct.findroot(e[i].v);
        if(fx!=fy)lct.link(e[i].u,i+n),lct.link(e[i].v,i+n);
        else
        {
            lct.split(e[i].u,e[i].v);
//            printf("%d--\n",lct.ch[e[i].v][0]);
            if(lct.ma[e[i].v]>e[i].b)
            {
                int p=lct.id[e[i].v]-n;
//                printf("%d %d++\n",lct.id[e[i].v],lct.ma[e[i].v]);
                lct.cut(e[p].u,p+n);lct.cut(e[p].v,p+n);
                lct.link(e[i].u,i+n),lct.link(e[i].v,i+n);
            }
        }
        fx=lct.findroot(1),fy=lct.findroot(n);
        if(fx==fy)
        {
            lct.split(1,n);
//            printf("%d \n",lct.ma[n]);
            ans=min(ans,e[i].a+lct.ma[n]);
        }
    }
    printf("%d\n",ans>=inf?-1:ans);
    return 0;
}
/********************
4 5
3 4 1 17
2 3 8 12
2 4 12 15
1 3 17 8
1 2 19 1
********************/

bzoj3669: [Noi2014]魔法森林 lct

标签:生成树   first   ota   syn   protector   联通   struct   noi   rev   

原文地址:https://www.cnblogs.com/acjiumeng/p/9118480.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!