【做题】Codeforces Round #436 (Div. 2) F. Cities Excursions——图论+dfs

题意:给你一个有向图,多次询问从一个点到另一个点字典序最小的路径上第k个点。

考虑枚举每一个点作为汇点(记为i),计算出其他所有点到i的字典序最小的路径。(当然,枚举源点也是可行的)

首先,我们建一张反向图,从i开始dfs,以删去所有无法到达i的点。

然后,因为此时图上所有点都可以到达i,所以可以贪心地在从每一个点出发的边中只保留终点编号最小的边,而把其他边删除。

这样就可以保证路径是字典序最小的,并且从每个点到i的路径只有一条。

而题目中给出了这样一种情况:

  • there are paths from sj to tj, but for every such path p there is another path q from sj to tj, such that pi > qi, where i is the minimum integer for which pi ≠ qi.

因此最后可能某些未被删去的点无法到达i,就需要从i开始再dfs一遍,以删去这些点。

在最后这次dfs的同时通过倍增记录路径(即祖先节点),以实现O(logn)地查询路径的第k个点。

时间复杂度O(n^2*logn+q*logn)。

  1 #include <bits/stdc++.h>
  2 #define travel(i,x,p) for(int i=p.fir[(x)];i;i=p.con[i].la)
  3 #define nex(x,p) p.con[x].b
  4 using namespace std;
  5 const int N=3010,Q=400010;
  6 struct edge{
  7     int la,b;
  8 };
  9 struct graph{
 10     edge con[N];
 11     int tot,fir[N];
 12     bool vis[N];
 13     void init()
 14     {
 15         tot=0;
 16         memset(fir,0,sizeof fir);
 17     }
 18     void add(int from,int to)
 19     {
 20         con[++tot].la=fir[from];
 21         con[tot].b=to;
 22         fir[from]=tot;
 23     }
 24     void dfs(int pos)
 25     {
 26         vis[pos]=1;
 27         for(int i=fir[pos];i;i=con[i].la)
 28         {
 29             if(!vis[con[i].b]) dfs(con[i].b);
 30         }
 31     }
 32 }p1,p2,p3;
 33 int n,m,q,anc[N][13];
 34 struct query{
 35     int s,k,id;
 36 };
 37 vector<query>ask[N];
 38 int ans[Q];
 39 void exdfs(int pos)
 40 {
 41     p3.vis[pos]=1;
 42     for(int i=1;i<=12;i++)
 43         anc[pos][i]=anc[anc[pos][i-1]][i-1];
 44     for(int i=p3.fir[pos];i;i=p3.con[i].la)
 45     {
 46         anc[p3.con[i].b][0]=pos;
 47         exdfs(p3.con[i].b);
 48     }
 49 }
 50 void doit(query tmp,int pos)
 51 {
 52     if(!p3.vis[tmp.s]) ans[tmp.id]=-1;
 53     else
 54     {
 55         tmp.k--;
 56         pos=tmp.s;
 57         for(int i=12;i>=0;i--)
 58         {
 59             if((1<<i)<=tmp.k)
 60                 tmp.k-=(1<<i),pos=anc[pos][i];
 61         }
 62         if(pos==0) ans[tmp.id]=-1;
 63         else ans[tmp.id]=pos;
 64     }
 65 }
 66 void solve(int pos)
 67 {
 68     memset(p2.vis,0,sizeof p2.vis);
 69     p2.dfs(pos);
 70     p3.init();
 71     int tmp;
 72     for(int i=1;i<=n;i++)
 73     {
 74         if(p2.vis[i]&&i!=pos)
 75         {
 76             tmp=n+1;
 77             travel(j,i,p1)
 78             {
 79                 if(p2.vis[nex(j,p1)]&&nex(j,p1)<tmp)
 80                     tmp=nex(j,p1);
 81             }
 82             if(tmp!=n+1) p3.add(tmp,i);
 83         }
 84     }
 85     memset(p3.vis,0,sizeof p3.vis);
 86     memset(anc,0,sizeof(anc));
 87     exdfs(pos);
 88     for(int i=0;i<(int)ask[pos].size();i++)
 89     {
 90         doit(ask[pos][i],pos);
 91     }
 92 }
 93 int main()
 94 {
 95     int from,to;
 96     scanf("%d%d%d",&n,&m,&q);
 97     for(int i=1;i<=m;i++)
 98     {
 99         scanf("%d%d",&from,&to);
100         p1.add(from,to);
101         p2.add(to,from);
102     }
103     int x,y,k;
104     for(int i=1;i<=q;i++)
105     {
106         scanf("%d%d%d",&x,&y,&k);
107         ask[y].push_back((query){x,k,i});
108     }
109     for(int i=1;i<=n;i++)
110         solve(i);
111     for(int i=1;i<=q;i++)
112         printf("%d
",ans[i]);
113     return 0;
114 }

小结:在诸如最小字典序路径和其他问题中,可以通过预处理以保证贪心的正确性。

原文地址:https://www.cnblogs.com/cly-none/p/7656368.html