ICode9

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

树上问题--kruskal重构树

2021-08-26 14:31:18  阅读:241  来源: 互联网

标签:重构 vis -- kruskal int maxn ff dis


前言:

其实之前就听说过这个算法,但是因为网上讲解的太少了,毕竟是个冷门算法,所以当时就没有去学习下去。昨天复习LCA的是时候看到有个题可以用这个算法,而且是特别裸的题,所以我就来学了。

参考blog:Kruskal重构树—学习笔记_niiick-CSDN博客_kruskal重构树

这个博客讲的挺好的,我就讲讲重点吧。

kruskal重构最重要的性质是:

LCA(u,v)的权值是 原图 u到v路径上最大/小边权的最小/大值(由建树时边权的排序方式决定)

kruskal一般用于解决这一类问题:

给你一个图,给你一个点,限制这个点不能走长度超过w的边,然后再问你一些问题。

这样讲可能有点抽象:

看一道kruskal经典题:

P4768 [NOI2018] 归程 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这道题很明显按降序先建kruskal重构树,因为低于某一海拔不能走了,也就是说明如果lca(u,v)<限制值,说明u走不到v,因为lca(u,v)是u->v路径上最小值的最大值。也就是说,当lca(u,v)>限制值,u能到达v。我们继续发现,我们让u往上爬,跑到一个祖先节点ff,如果ff>限制值,说明u能到达ff所在子树的所有节点,我们找到u的满足条件的最远祖先,那么这个点包含的节点就是u能走到的所有节点。于是再跑个最短路就行了。讲实话代码有点繁杂。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
int n,m,lastans,cntx,totx,q,k,s;
int first[maxn],dep[maxn],ff[maxn][22],head[maxn],f[maxn];
struct 
{
    int l,a;
}p[maxn];
int dis[maxn],vis[maxn];
struct edge
{
    int v,w,a,u,nxt;
}e[maxn<<2];
struct edge1
{
    int v,w,a,u,nxt;
}t[maxn<<2],tr[maxn<<2];
void addx(int u,int v,int w)
{
    totx++;
    t[totx].v=v;
    t[totx].w=w;
    t[totx].nxt=head[u];
    head[u]=totx;
}
void add(int u,int v)
{
    cntx++;
    tr[cntx].v=v;
    tr[cntx].nxt=first[u];
    first[u]=cntx;
}
bool cmp(edge a,edge b)
{
    return a.a>b.a;
}
int find(int x){return (x==f[x])?x:f[x]=find(f[x]);}
void dfs(int u,int fa)
{
    dep[u]=dep[fa]+1;
    ff[u][0]=fa;
    for(int i=1;i<=20;i++)
    ff[u][i]=ff[ff[u][i-1]][i-1];
    for(int i=first[u];i;i=tr[i].nxt)
    {
        int v=tr[i].v;
        dfs(v,u);
        p[u].l=min(p[u].l,p[v].l);
    }
}
int query(int x,int y)
{
    for(int i=19;i>=0;i--) if(dep[x]-(1<<i)>0 && p[ff[x][i]].a>y) x=ff[x][i];
    return p[x].l;
}
void kruskal()
{
    int tot=0,cnt=n;
    for(int i=1;i<=(n<<1);i++) f[i]=i;
    sort(e+1,e+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        int u=e[i].u;
        int v=e[i].v;
        int fx=find(u);
        int fy=find(v);
        if(fx!=fy)
        {
            add(++cnt,fx);
            add(cnt,fy);
            f[fy]=f[fx]=cnt;
            p[cnt].a=e[i].a;
            tot++;
        }
        if(tot==n-1) break ;
    }
    dfs(cnt,0);
    while(q--)
    {
        int x1,x2;
        cin>>x1>>x2;
         int x=(k*lastans+x1-1)%n+1;
         int y=(k*lastans+x2)%(s+1);
    cout<<(lastans=query(x,y));
    cout<<endl;
    }
}
struct node
{
    int dis,id;
    bool operator < (const node &a) const
    {
        return dis>a.dis;
    }
};
priority_queue<node> qq;
void dij(int s)
{
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0; 
    node u;
    u.id=s;
    u.dis=0;
    qq.push(u);
    while(qq.size())
    {
        node u=qq.top();
        qq.pop();
        if(vis[u.id]) continue ;
        vis[u.id]=1;
        for(int i=head[u.id];i;i=t[i].nxt)
        {
            if(!vis[t[i].v] && dis[u.id]+t[i].w<dis[t[i].v])
            {
                dis[t[i].v]=dis[u.id]+t[i].w;
                qq.push(node{dis[t[i].v],t[i].v});
            }
        }
    }
    for(int i=1;i<=n;i++) p[i].l=dis[i];
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        lastans=0;
        cin>>n>>m;
        memset(e,0,sizeof(e));
        cntx=0;
        totx=0;
        memset(first,0,sizeof(first));
        memset(head,0,sizeof(head));
        memset(f,0,sizeof(f));
        for(int i=1;i<=m;i++) 
        {
            cin>>e[i].u>>e[i].v>>e[i].w>>e[i].a;
            addx(e[i].u,e[i].v,e[i].w);
            addx(e[i].v,e[i].u,e[i].w);
        }
        for(int i=n+1;i<=(n<<1);i++) p[i].l=0x3f3f3f3f;
        dij(1);
        cin>>q>>k>>s;
        kruskal();
    }   
    return 0;
}
View Code

再看一道经典题:P4197 Peaks - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这个就很裸了,第k大就是主席树呗(说的很简单,其实我不太会打主席树,所以我没打代码),然后就是个大水题。

标签:重构,vis,--,kruskal,int,maxn,ff,dis
来源: https://www.cnblogs.com/simpleton/p/15189342.html

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

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

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

ICode9版权所有