codeforces864F Cities Excursions

190 篇文章 2 订阅
71 篇文章 0 订阅

题面

题意

给出一张有向图,多次询问,每次询问给出三个数u,v,k,表示询问从u到v的路径上第k个点是什么。
若u,v不连通或长度大于k或该路径无限长输出-1

做法

首先可以通过n次dfs求出数组ok,ok[i][j]表示从点i出发是否可到达点j,然后将询问离线。
对于以点v为终点的询问,可以对于所有可以到达它的点u(u!=v),找到点u直接连接,字典序最小且可以到达点v的点t,并让t向v连一条边,这些边必然会形成一棵以v为根的树,而且无法到达v或路径无限长的点显然不在树上,u到v的第k个点就是u的第k个祖先,用倍增求出即可。

代码

#include<bits/stdc++.h>
#define LG 12
#define N 3010
#define M 400100
using namespace std;

int n,m,Q,bb,deep[N],first[N],ans[M],fa[N][15];
bool ok[N][N],vis[N];
struct Bn
{
    int to,next;
}bn[N];
struct Que
{
    int u,k,id;
};
vector<int>to[N];
vector<Que>que[N];

inline void add(int u,int v)
{
    bb++;
    bn[bb].to=v;
    bn[bb].next=first[u];
    first[u]=bb;
}

void dfs(int now,int fr)
{
    if(ok[fr][now]) return;
    ok[fr][now]=1;
    int i,t;
    for(i=0;i<to[now].size();i++)
    {
		t=to[now][i];
		dfs(t,fr);
    }
}

void Dfs(int now)
{
    int p,q;
    vis[now]=1;
    for(p=first[now];p!=-1;p=bn[p].next)
    {
		q=bn[p].to;
		fa[q][0]=now;
		deep[q]=deep[now]+1;
		Dfs(q);
    }
}

inline int jump(int u,int v)
{
    int i;
    for(i=LG;v;i--)
    {
		if(v>=(1 << i))
		{
		    v-=(1 << i);
		    u=fa[u][i];
		}
    }
    return u;
}

void work(int now)
{
    bb=0;
    memset(first,-1,sizeof(first));
    memset(vis,0,sizeof(vis));
    int i,j,t;
    for(i=1;i<=n;i++)
    {
		if(!ok[i][now] || i==now) continue;
		for(j=0;j<to[i].size();j++)
		{
		    t=to[i][j];
		    if(ok[t][now]) break;
		}
		if(j==to[i].size()) puts("gg");
		add(t,i);
    }
    deep[now]=1;
    Dfs(now);
    for(i=1;i<=LG;i++)
    {
		for(j=1;j<=n;j++)
		{
		    fa[j][i]=fa[fa[j][i-1]][i-1];
		}
    }
    for(i=0;i<que[now].size();i++)
    {
		Que tmp=que[now][i];
		if(!vis[tmp.u] || tmp.k>deep[tmp.u])
		{
		    ans[tmp.id]=-1;
		    continue;
		}
		ans[tmp.id]=jump(tmp.u,tmp.k-1);
    }
}

int main()
{
    int i,j,p,q,o;
    cin>>n>>m>>Q;
    for(i=1;i<=m;i++)
    {
		scanf("%d%d",&p,&q);
		to[p].push_back(q);
    }
    for(i=1;i<=n;i++) sort(to[i].begin(),to[i].end());
    for(i=1;i<=n;i++) dfs(i,i);
    for(i=1;i<=Q;i++)
    {
		scanf("%d%d%d",&p,&q,&o);
		if(!ok[p][q]) ans[i]=-1;
		else que[q].push_back((Que){p,o,i});
    }
    for(i=1;i<=n;i++) work(i);
    for(i=1;i<=Q;i++) printf("%d\n",ans[i]);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值