ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P2387 [NOI2014] 魔法森林

2021-04-25 23:36:12  阅读:177  来源: 互联网

标签:P2387 int void 魔法 tr rs NOI2014 maxi ls


【题意】

一张无向图,每个边有两种边权,求1-n的路径中,经过两种边权最大值的和最小是多少

【分析】

这道题很巧妙地模仿了生成树的思想,先按照一个边权b排序,然后依次加入,这样能保证每一次的bi的最大值是不用计算的,就是当前加的bi值

只要维护ai最大值最小即可,所以我们利用LC不断加边,当x,y已经联通时,我们找到x-y路径中ai最大的一个,断开,再连上新的当前的x-y边

所以我们的LCT需要维护一个路径中的最大值出现在哪条边

【代码】

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=5e4+5;
const int maxm=2e5+5;
const int inf=0x3f3f3f3f;
struct edge
{
    int x,y,a,b;
}e[maxm];
bool cmp(edge aa,edge bb)
{
    return aa.b<bb.b;
}
int val[maxm],fa[maxm];
struct LCT
{
    int son[2],v,maxi,rev,f;
    LCT() { f = son[0] = son[1] = rev = 0; }
}tr[maxm];
#define ls(x) tr[x].son[0]
#define rs(x) tr[x].son[1]
int find(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
int q[maxm],len;
int isroot(int x)
{
    return tr[x].f&&( ls(tr[x].f)==x || rs(tr[x].f)==x);
}
void pushup(int x)
{
    tr[x].maxi=x;
    if(ls(x) && val[tr[ls(x)].maxi]>val[tr[x].maxi]) tr[x].maxi=tr[ls(x)].maxi;
    if(rs(x) && val[tr[rs(x)].maxi]>val[tr[x].maxi]) tr[x].maxi=tr[rs(x)].maxi;
}
void pushdown(int x)
{
    if(!tr[x].rev) return;
    swap(ls(x),rs(x));
    if(ls(x)) tr[ls(x)].rev^=1;
    if(rs(x)) tr[rs(x)].rev^=1;
    tr[x].rev^=1;
}
void rotate(int x) {
    int y =tr[x].f,z=tr[y].f,b=ls(y)==x?rs(x):ls(x);
    if (z && isroot(y)) (ls(z) == y ? ls(z) : rs(z)) = x;
    tr[x].f=z; tr[y].f=x; b?tr[b].f=y:0;
    if (ls(y) == x) rs(x)= y,ls(y)= b;
    else ls(x) = y, rs(y) = b;
    pushup(y);
    pushup(x);
}
int which(int x)
{
    return x==rs(tr[x].f);
}
void splay(int x) 
{
    int i, y; q[len = 1] = x;
    for(int y=x;isroot(y);y=tr[y].f) q[++len]=tr[y].f;
    for(int i=len;i>=1;i--) pushdown(q[i]);
    while(isroot(x))
    {
        if(isroot(tr[x].f))
        {
            if (which(x) == which(tr[x].f)) rotate(tr[x].f);
            else rotate(x);
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x)
{
    for(int y=0;x;y=x,x=tr[x].f)
    {
        splay(x);
        rs(x)=y;
        if(y) tr[y].f=x;
        pushup(x);
    }
}
void makeroot(int x)
{
    access(x);
    splay(x);
    tr[x].rev^=1;
}
void link(int x,int y)
{
    makeroot(x);
    tr[x].f=y;
}
void cut(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    tr[y].son[0]=0; tr[x].f=0;
    pushup(y);
}
int path(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    return tr[y].maxi;
}
int main()
{
//    freopen("forest.in","r",stdin);
//    freopen("forest.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].a,&e[i].b);
    sort(e+1,e+m+1,cmp);
    int ans=inf;
    for(int i=1;i<=m+n;i++) fa[i]=tr[i].maxi=i;
    for(int i=n+1;i<=n+m;i++) val[i]=e[i-n].a;
    for(int i=1;i<=m;i++)
    {
        int u=e[i].x,v=e[i].y,flag=1;
        if(find(u)==find(v))
        {
            int maxi=path(u,v);
            if(val[maxi]>e[i].a)
            {
                cut(e[maxi-n].x,maxi);
                cut(maxi,e[maxi-n].y);
            }
            else flag=0;
        }
        else
        {
            int fx=find(u),fy=find(v);
            if(fx!=fy) fa[fx]=fy;
        }
        if(flag)
        {    
            link(u,i+n);
            link(i+n,v);
        }
        if(find(1)==find(n))
            ans=min(ans,e[i].b+val[path(1,n)]);
    }
    printf("%d\n",ans!=inf?ans:-1);
    return 0;
}

 

吐槽:这道题目利用了并查集去加速判断,其实findroot也可,只是常数比较大,像这种只增加连通性的,最好都用并查集,有的题会卡一下下

这个并查集的fa和LCT的f一定要区分好,调了好久!!!

标签:P2387,int,void,魔法,tr,rs,NOI2014,maxi,ls
来源: https://www.cnblogs.com/andylnx/p/14702645.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有