Codeforces 864F - Cities Excursions 贪心 and 构造

题意:

题意: 给定一个有向图,询问由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

感谢@凌幽 提供的翻译(洛谷用户)

解:

1、如果从s贪心的走形成环了,那么之后的点一定不会从s出发达到。

2、询问第k个点,在树上可以用lca的祖先数组倍增来完成询问。

3、可以倒着思考这个问题,考虑所有可以贪心到达,t的节点,---->会构成一课反向树,

整个树上的所有节点都是可以出发到达跟的节点,

4、考虑将所有的询问排序,我们那么第k节点就是第K-1个祖先,。

 

#include<bits/stdc++.h>
using namespace std;
const int maxn =3e3+10;
const int logm=18;
int n,m,q,ans[412345],anc[maxn][logm],dep[maxn],vis[maxn];
vector<int>vec[maxn],vec_r[maxn];
int rd(){int tt;scanf("%d",&tt);return tt;}
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
bool ok[maxn][maxn];
struct node{int x,k,ind;};
vector<node>que[maxn];
void Dfs(int x){
    vis[x]=1;
    for(int i=0;i<vec_r[x].size();i++){
        int y=vec_r[x][i];
        anc[y][0]=x;
        dep[y]=dep[x]+1;
        for(int j=1;j<logm;j++){
            anc[y][j]=anc[anc[y][j-1]][j-1];
        }
        Dfs(y);
    }
}
int ask(int x,int k){
    for(int i=logm-1;i>=0;--i){
        if((k>>i)&1){
            x=anc[x][i];
        }
    }return x;
}
void work(int x){
    if(que[x].size()==0)return ;
    mem(dep,0);dep[x]=1;
    mem(vis,0);
    for(int i=1;i<=n;i++){vec_r[i].clear();}
    for(int i=1;i<=n;i++){
        if(!ok[i][x] or i==x)continue;
        for(int j=0;j<vec[i].size();j++){
            int tem=vec[i][j];
            if(ok[tem][x])
            {
                vec_r[tem].pb(i);break;
            }
        }
    }
    Dfs(x);

    for(int i=0;i<que[x].size();i++){
        int xx=que[x][i].x;
        if(dep[xx]<que[x][i].k or !vis[xx]){ans[que[x][i].ind]=-1;continue;}
        ans[que[x][i].ind]=ask(xx,que[x][i].k-1);
    }
}

void dfs(int x,int from){
    if(ok[from][x])return ;
    ok[from][x]=1;
    for(int i=0;i<vec[x].size();i++){
        int y=vec[x][i];
        dfs(y,from);
    }
}
signed main(){
    #ifdef swt
        freopen("input2.txt","r",stdin);
    #endif
    #define en '\n'
    cin>>n>>m>>q;
    for(int i=1;i<=m;i++){int x=rd(),y=rd();vec[x].pb(y);}
    for(int i=1;i<=n;i++){sort(vec[i].begin(),vec[i].end());}
    for(int i=1;i<=n;i++)dfs(i,i);
    for(int i=1;i<=q;i++){
        int s=rd(),t=rd(),k=rd();
        if(!ok[s][t]){ans[i]=-1;
        continue;
        }
        que[t].pb((node){s,k,i});
    }
    for(int i=1;i<=n;i++){work(i);}
    for(int i=1;i<=q;i++){
        cout<<ans[i]<<en;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值