[NOI2014]魔法森林

题目

[NOI2014]魔法森林

解析

要求a 与 b 的总和最小 可以按a排序 再以b为权值维护一颗树 LCT维护最小生成树
要解决的问题

  1. 将每一条边变为一个点 边权变为点权 举个栗子: u->v有一条边权为w的边 怎LCT连边方式为 u->new->v new的点权为w
  2. 不断维护最小生成树 如果新加入的边的 u与v不连通 直接加 如果连通 求u到v路上的最大边权边 将它切掉(其实是切两条边)再加边
  3. 边权为b

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100000+10;
int n,m,blow;
int ans=0x7fffffff;
struct node1
{
    int a,b,start,end;
    bool operator < (const node1 & o) const
    {
        return a<o.a;
    }
}edge[maxn];
struct node
{
    int fa,ch[2]; bool reverse,is_root;
    int data,id; int max1,id1;
}T[maxn*10];
int getson(int x)
{
    return T[T[x].fa].ch[1]==x; 
}
void update(int x)
{
    if(x>n)
        T[x].max1=T[x].data,T[x].id1=T[x].id;
    else T[x].max1=0;
    if(T[x].ch[1] && T[x].max1<T[T[x].ch[1]].max1)
    {
        T[x].max1=T[T[x].ch[1]].max1; T[x].id1=T[T[x].ch[1]].id1;
    }
    if(T[x].ch[0] && T[x].max1<T[T[x].ch[0]].max1)
    {
        T[x].max1=T[T[x].ch[0]].max1; T[x].id1=T[T[x].ch[0]].id1;
    }
}
void rotate(int x)
{
    int son=getson(x),fa=T[x].fa; int g=T[fa].fa;
    T[fa].ch[son]=T[x].ch[son^1]; T[fa].fa=x; T[x].fa=g;
    if(T[x].ch[son^1]) T[T[x].ch[son^1]].fa=fa; 
    T[x].ch[son^1]=fa;
    if(!T[fa].is_root) T[g].ch[T[g].ch[1]==fa]=x;
    else T[x].is_root=true,T[fa].is_root=false;
    update(fa); update(x);
}
void pushreverse(int x)
{
    if(!x) return;
    swap(T[x].ch[0],T[x].ch[1]);
    T[x].reverse^=1;
}
void pushdown(int x)
{
    if(T[x].reverse)
    {
        pushreverse(T[x].ch[0]); pushreverse(T[x].ch[1]);
        T[x].reverse=false;
    }
}
void push(int x)
{
    if(!T[x].is_root) push(T[x].fa);
    pushdown(x);
}
void splay(int x)
{
    push(x);
    for(int fa; !T[x].is_root; rotate(x))
    {
        if(!T[fa=T[x].fa].is_root) rotate(getson(fa)==getson(x)? fa : x);
    }
}
void access(int x)
{
    for(int y=0; x; x=T[y=x].fa)
    {
        splay(x); T[T[x].ch[1]].is_root=true; T[T[x].ch[1]=y].is_root=false; update(x);
    }
}
void mroot(int x)
{
    access(x); splay(x); pushreverse(x);
}
int findroot(int x)
{
    while(T[x].fa)
        x=T[x].fa;
    return x;
}
void link(int x,int y)
{
    mroot(x); T[x].fa=y;
}
void cut(int x,int y)
{
    mroot(x); access(y); splay(y);
    T[x].is_root=true; T[x].fa=T[y].ch[0]=0;
}
int solve(int x)
{
    blow=x;
        int x1=edge[blow].start,y=edge[blow].end;
        int xx=findroot(x1),yy=findroot(y);
//      cout<<x<<' '<<x1<<' '<<y<<' '<<xx<<' '<<yy<<endl;
        if(xx!=yy)
        {
            T[blow+n].data=edge[blow].b; T[blow+n].id=blow+n; link(n+blow,x1); link(n+blow,y);
        }
        else
        {
            mroot(x1);
            access(y);
            splay(y);
            if(T[y].max1>edge[blow].b)
            {
//              cout<<x<<' '<<T[y].id1<<endl;
                T[T[y].id1].id=T[T[y].id1].data=0;
                int cc=T[y].id1;//¼Çϵ±Ç°½Úµã£¬cutÖ®ºó¿ÉÄÜ»á¸Ä±ä 
                cut(edge[cc-n].start,cc); cut(cc,edge[cc-n].end);
                T[blow+n].data=edge[blow].b; T[blow+n].id=blow+n; link(x1,n+blow); link(n+blow,y);
            }
        }
    xx=findroot(1),yy=findroot(n);
    if(xx!=yy) return -1;
    mroot(1); access(n); splay(n);
    return T[n].max1;
}
int main()
{
    freopen("magicalforest.in","r",stdin);
    freopen("magicalforest.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=0; i<=n+m; i++)
    {
        T[i].is_root=true;
    }
    for(int i=1; i<=m; i++)
    {
        scanf("%d %d %d %d",&edge[i].start,&edge[i].end,&edge[i].a,&edge[i].b);
    }
    sort(edge+1,edge+m+1);
    bool flag=false; blow=1;
    for(int i=1; i<=m; i++)
    {
/*      if(i==327)
        {
            int tmp=solve(i);
            if(tmp!=-1 && tmp+i<ans)
            {
                flag=true;
                ans=min(ans,tmp+edge[i].a);
            }
            continue;
        }*/
        int tmp=solve(i);
        if(tmp!=-1 && tmp+edge[i].a<ans)
        {
            flag=true;
            ans=min(ans,tmp+edge[i].a);
        }
    }
    cout<<(flag? ans:-1)<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值