[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;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页