题意:给你一个有向图,多次询问从一个点到另一个点字典序最小的路径上第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 }
小结:在诸如最小字典序路径和其他问题中,可以通过预处理以保证贪心的正确性。