[NOI2018]归程(kruskal重构树)

  • 题意:每天给定一个起点,终点为1,每天可以开汽车过一段海拔高于当天水位线的路,剩下的路步行,问步行最短距离,强制在线。
  • 思路:每天的最短距离相当于每天走高于海拔高于水位线的路所能到达的所有点中到1的最短距离。现在要想办法能够直接查询这个最短距离,如果能根据海拔构造一种树状结构,那么每次能开车到达的点对应一颗子树,那么只需要倍增出这颗子树的根节点就可以了。
  • kruskal重构树:对海拔从大到小排序,然后跑kruskal,在求这颗最大生成树的过程中每次两个点不在一个集合,就引入一个新的结点,作为这两个点的代表点的父亲,点权为边权,注意在这个过程中要为护重构树的形态,重构树具有一下性质:
    1.树上的每条链具有单调性(与排序有关);
    2原来图中的结点全部对应重构树中的叶子结点;
    在这里插入图片描述

总结:将海拔按照从大到小排序,构造克鲁斯卡尔重构树(注意形态),一次dfs求出以每个结点为根时子树中的点到1的最短路,对于一组询问v,p,通过树上倍增找到这个对应子树(注意,在水位线为p的前提下,v不可能只走海拔大于p的路径达到非这颗子树上的点,一开始我没注意到这点),得到解。

#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
int t,n,m,last[200005],cnt,d[200005],xx,yy,z,zz,fa[400005],num,Q,K,S,v0,p0,f[400005][25],dep[400005];
struct edge{
    int u,v,w,next,a;
}e[800005];
struct heapnode{
    int u,d;
    bool operator<(const heapnode &a)const{
        return d>a.d;}
};
struct node{
    int l,r,md,pows;
}no[400005];
inline void add(int u,int v,int w,int a)
{
    e[++cnt].v=v;
    e[cnt].next=last[u];
    e[cnt].u=u;
    e[cnt].w=w;
    e[cnt].a=a;
    last[u]=cnt;
}
bool cmp(edge a,edge b)
{
    return a.a>b.a;
}
int finds(int x)
{
    return fa[x]==x?x:fa[x]=finds(fa[x]);
}
void dijkstra()
{
    d[1]=0;
    for(int i=2;i<=n;i++)
    d[i]=0x3f3f3f3f;
    priority_queue<heapnode>q;
    q.push({1,0});
    while(!q.empty())
    {
        heapnode x=q.top();q.pop();
        if(x.d!=d[x.u]) continue;
        int u=x.u;
        for(int i=last[u];i;i=e[i].next)
        {
            int v=e[i].v,w=e[i].w;
            if(d[u]+w<d[v])
            {
                d[v]=d[u]+w;
                q.push({v,d[v]});
            }
        }
    }
}
void kruskal()
{
    num=n;
    sort(e+1,e+1+cnt,cmp);
    int k=0;
    for(int i;k!=n-1;i++)
    {
        int u=e[i].u,v=e[i].v;
        if(finds(u)!=finds(v))
        {
            num++;
            no[num].l=finds(u);
            no[num].r=finds(v);
            no[num].pows=e[i].a;
            fa[finds(v)]=num;
            fa[finds(u)]=num;
            k++;
        }
    }
}
int dfs(int u,int pa)
{
    dep[u]=dep[pa]+1;f[u][0]=pa;
    for(int i=1;i<=19;i++)
    f[u][i]=f[f[u][i-1]][i-1];
    if(u<=n)
    {
        no[u].md=d[u];
        return d[u];
    }
    int minn=0x3f3f3f3f;
    minn=min(dfs(no[u].l,u),minn);
    minn=min(dfs(no[u].r,u),minn);
    no[u].md=minn;
    return minn;
}
int query(int x,int y)
{
    for(int i=19;i>=0;i--)
    if(dep[x]-(1<<i)>0&&no[f[x][i]].pows>y) x=f[x][i];
    return no[x].md;
}
void solve()
{
    dfs(num,0);
    for(int i=1;i<=n;i++)
    no[i].pows=0x3f3f3f3f;
    int ans=0,v,p;
    for(int i=1;i<=Q;i++)
    {
        scanf("%d%d",&v0,&p0);
        v=(v0+K*ans-1)%n+1;
        p=(p0+K*ans)%(S+1);
        ans=query(v,p);
        cout<<ans<<endl;
    }
}
void intt()
{
    memset(e,0,sizeof(e));
    memset(no,0,sizeof(no));
    memset(last,0,sizeof(last));
    memset(dep,0,sizeof(dep));
    memset(f,0,sizeof(f));
    cnt=num=0;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&xx,&yy,&z,&zz);
            add(xx,yy,z,zz);
            add(yy,xx,z,zz);
        }
        dijkstra();
        for(int i=1;i<=2*n;i++)
        fa[i]=i;
        scanf("%d%d%d",&Q,&K,&S);
        kruskal();
        solve();
        intt();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希表扁豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值