[NOI2018]归程

题目链接

https://www.luogu.org/problemnew/show/P4768

https://www.luogu.org/problemnew/show/U31655

吐槽

垃圾出题人卡什么spfa……而且还是能卡的点全都卡了,用spfa直接60分……

题解

kruskal生成树+倍增+堆优化dijkstra。

首先介绍一下kruskal生成树:

在用kruskal求最小生成树的时候,我们需要先把边排序,每次选长度最小的边,如果连接了两个不同的块,那么选取这条边。

例如下面的这个图:

图1

那么kruskal就将会选取1,2,4三条边。

而kruskal生成树就是对每个点建立一棵树,在kruskal选取一条边的时候,将两个连通块的根的父亲都设为这条边。

例如,上面那个图的kruskal生成树就是:

图2

其中方点代表边,而圆点代表原图中的点。

kruskal生成树的性质:由于从下到上边权越来越大,因此某一条边的子树中边权的最大值是一定的。

回到这道题,如果我们用kruskal对海拔建立一棵最大生成树,那么某一条边在kruskal生成树中子树的海拔最小值就是自己的海拔。因此,从一个点出发能到达的所有点是kruskal生成树的一个子树或一个点。

倍增求出一个点能到达的海拔最低的边,预处理出kruskal生成树中一条边的子树中最小的dist,接下来就很好求了。

时间复杂度O(nlogn+qlogm+mα(n)logm)O(nlog⁡n+qlog⁡m+mα(n)log⁡m)

代码

#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>

const int maxn=200000;
const int maxm=400000;
const int inf=0x3f3f3f3f;

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

namespace dsu
{
  int fa[maxn*2+10];

  int clear()
  {
    memset(fa,0,sizeof fa);
    return 0;
  }

  int find(int x)
  {
    return fa[x]?fa[x]=find(fa[x]):x;
  }
}

struct edge
{
  int u,v,l;

  edge(int u_=0,int v_=0,int l_=0)
  {
    u=u_;
    v=v_;
    l=l_;
  }

  bool operator <(const edge &other) const
  {
    return l<other.l;
  }
};

int n,m;

namespace tree
{
  int pre[maxn*2+10],now[maxn*2+10],son[maxn*2+10],tot;
  int val[maxn+10],fa[maxn*2+10][20],dist[maxn*2+10];

  int clear()
  {
    tot=0;
    memset(now,0,sizeof now);
    memset(val,0,sizeof val);
    return 0;
  }

  int ins(int a,int b)
  {
    pre[++tot]=now[a];
    now[a]=tot;
    son[tot]=b;
    return 0;
  }

  int getval()
  {
    for(int i=1; i<=18; ++i)
      {
        for(int j=1; j<=2*n+1; ++j)
          {
            fa[j][i]=fa[fa[j][i-1]][i-1];
          }
      }
    return 0;
  }

  int dfs(int u,int f)
  {
    fa[u][0]=f;
    if(u>=n)
      {
        return 0;
      }
    int j=now[u];
    dist[u]=inf;
    while(j)
      {
        int v=son[j];
        dfs(v,u);
        dist[u]=std::min(dist[u],dist[v]);
        j=pre[j];
      }
    return 0;
  }

  int getdist(int v,int p)
  {
    v=v+n-1;
    if(val[fa[v][0]]<=p)
      {
        return dist[v];
      }
    v=fa[v][0];
    for(int i=18; i>=0; --i)
      {
        if(fa[v][i]&&(val[fa[v][i]]>p))
          {
            v=fa[v][i];
          }
      }
    return dist[v];
  }
}

struct data
{
  int dist,id;

  data(int dist_=0,int id_=0)
  {
    dist=dist_;
    id=id_;
  }

  bool operator <(const data &other) const
  {
    return dist>other.dist;
  }
};

namespace graph
{
  int pre[maxm*2+10],now[maxn+10],son[maxm*2+10],tot;
  int dist[maxn+10],val[maxm*2+10],cnt,vis[maxn+10];
  edge e[maxm+10];
  std::priority_queue<data> h;

  int clear()
  {
    tot=cnt=0;
    memset(now,0,sizeof now);
    return 0;
  }

  int ins(int a,int b,int c)
  {
    pre[++tot]=now[a];
    now[a]=tot;
    son[tot]=b;
    val[tot]=c;
    return 0;
  }

  int add(int a,int b,int c,int d)
  {
    ins(a,b,c);
    ins(b,a,c);
    e[++cnt]=edge(a,b,d);
    return 0;
  }

  int kruskal()
  {
    std::sort(e+1,e+cnt+1);
    int need=n-1;
    for(int i=cnt; i; --i)
      {
        int fu=dsu::find(e[i].u+n-1),fv=dsu::find(e[i].v+n-1);
        if(fu!=fv)
          {
            dsu::fa[fu]=dsu::fa[fv]=need;
            tree::ins(need,fu);
            tree::ins(need,fv);
            tree::val[need]=e[i].l;
            --need;
            if(!need)
              {
                break;
              }
          }
      }
    return 0;
  }

  int dijkstra(int s)
  {
    memset(dist,63,sizeof dist);
    dist[s]=0;
    memset(vis,0,sizeof vis);
    h.push(data(0,s));
    while(!h.empty())
      {
        data w=h.top();
        h.pop();
        if(vis[w.id])
          {
            continue;
          }
        vis[w.id]=1;
        int j=now[w.id];
        while(j)
          {
            int v=son[j];
            if(dist[w.id]+val[j]<dist[v])
              {
                dist[v]=dist[w.id]+val[j];
                h.push(data(dist[v],v));
              }
            j=pre[j];
          }
      }
    for(int i=1; i<=n; ++i)
      {
        tree::dist[i+n-1]=dist[i];
      }
    return 0;
  }
}

int main()
{
  int t=read();
  while(t--)
    {
      dsu::clear();
      tree::clear();
      graph::clear();
      n=read();
      m=read();
      while(m--)
        {
          int u=read(),v=read(),l=read(),h=read();
          graph::add(u,v,l,h);
        }
      graph::kruskal();
      graph::dijkstra(1);
      tree::dfs(1,0);
      tree::getval();
      int q=read(),k=read(),s=read();
      int lastans=0;
      while(q--)
        {
          int v=read(),p=read();
          v=(v+lastans*k-1)%n+1;
          p=(p+lastans*k)%(s+1);
          lastans=tree::getdist(v,p);
          printf("%d\n",lastans);
        }
    }
  return 0;
}

转载于:https://www.cnblogs.com/Canopus-wym/p/10376158.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值