Codeforces 864F. Cities Excursions 倍增

题意

题意: 给定一个有向图,询问由s到t的字典序最小的路径的第k个点 输入: 第一行三个整数n(2<=n<=3000),m(0<=m<=3000),q(1<=q<=400000) 接下来m行每行两个整数x,y(1<=x,y<=n且x≠y)表示由x到y有一条路(由城市x到y最多只有一条路) 接下来q行每行三个整数s,t(1<=s,t<=n且s≠t),k(1<=k<=3000) 输出: q行,每行对应一个答案 若s与t不连通输出-1 若字典序最小的路径构成环输出-1

分析

考虑只有一个询问的话,你每一次从x走到一个那些能到达y的点中的最小的一个
这个东西显然可以倍增处理,如果在环上,则走n个点后还能走,puts(“-1”)就好了
对于询问离线,枚举每个y,反着bfs一次,然后对于每个询问正着倍增
时间复杂度

O(nmlogn+qlogn) O ( n m l o g n + q l o g n )

代码

#include <bits/stdc++.h>
#define ll long long
#define pb push_back

using namespace std;

const int N = 3010;

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

struct node{int x,y,next;}edge[N]; int len,first[N];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}

struct node1{int x,y,k,id;}p[400010]; int fa[N][13];

int n,m,q; vector<int>v[N],g[N];

queue<int> Q; bool vis[N];
void bfs(int x)
{
  while(!Q.empty()) Q.pop(); Q.push(x);
  memset(vis,0,sizeof(vis)); vis[x]=1;
  memset(fa,63,sizeof(fa)); fa[x][0]=0;
  while(!Q.empty())
  {
    x=Q.front();
    for(auto i:g[x])
    {
      if(!vis[i]){vis[i]=1; fa[i][0] = x; Q.push(i);}
      else fa[i][0] = min(fa[i][0] , x);
    }
    Q.pop();
  }
}

int ans[400010];

int main()
{

  n=read(); m=read(); q=read();
  len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=m;i++){int x=read(); int y=read(); ins(x,y); g[y].pb(x);}
  for(int i=1;i<=q;i++) p[i].x=read(),p[i].y=read(),p[i].k=read(),v[p[i].y].pb(i),p[i].id=i;
  for(int y=1;y<=n;y++)
  {
    bfs(y);
    for(int j=1;j<=12;j++) for(int x=1;x<=n;x++) if(fa[x][j-1] <= n && fa[fa[x][j-1]][j-1] <= n) fa[x][j] = fa[fa[x][j-1]][j-1];
    for(auto i:v[y])
    {
      int x = p[i].x;
      if(fa[x][12]<=n || !vis[x]){ans[p[i].id]=-1; continue;} p[i].k--;
      for(int k=12;k>=0;k--) if(p[i].k >= (1<<k)){p[i].k-=(1<<k),x=fa[x][k]; if(x>n) break;}
      if(x>n || x==0){ans[p[i].id]=-1; continue;}
      else ans[p[i].id] = x;
    }
  }

  for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值