题目大意:
一个$n(nle3000)$个点的有向图,$q(qle4 imes10^5)$组询问,每次询问$s_i,t_i$之间是否存在一条字典序最小的路径(可以重复经过不为$t_i$的结点)。若存在,求出该路径上经过的第$k_i$个结点。
思路:
将原图的边反向。考虑根据$t_i$对所有询问进行分组。对于$t_i$相同的询问,在反向图中DFS,求出每个结点到$t_i$的最小字典序路径中的下一个结点是多少,这可以转化为一个树形结构。若$s_i$与$t_i$不连通,则说明路径不存在;若$s_i$的第$2^{lfloorlog_2n
floor+1}$级祖先存在,则说明存在环。询问第$k_i$个结点可以树上倍增。
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<forward_list> 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 constexpr int N=3001,Q=4e5,logN=13; 13 std::forward_list<int> e[N]; 14 struct Query { 15 int s,k,id; 16 }; 17 std::forward_list<Query> q[N]; 18 int ans[Q],anc[N][logN]; 19 inline int lg2(const float &x) { 20 return ((unsigned&)x>>23&255)-127; 21 } 22 void dfs(const int &x,const int &par,const int &s) { 23 anc[x][0]=par; 24 for(auto &y:e[x]) { 25 if(y==s||(anc[y][0]&&anc[y][0]<=x)) continue; 26 dfs(y,x,s); 27 } 28 } 29 int main() { 30 const int n=getint(),m=getint(),cnt_q=getint(),lim=lg2(n)+1; 31 for(register int i=0;i<m;i++) { 32 const int u=getint(),v=getint(); 33 e[v].push_front(u); 34 } 35 for(register int i=0;i<cnt_q;i++) { 36 const int s=getint(),t=getint(),k=getint(); 37 q[t].push_front({s,k-1,i}); 38 } 39 for(register int i=1;i<=n;i++) { 40 if(q[i].empty()) continue; 41 memset(anc,0,sizeof anc); 42 dfs(i,0,i); 43 for(register int j=1;j<=lim;j++) { 44 for(register int i=1;i<=n;i++) { 45 anc[i][j]=anc[anc[i][j-1]][j-1]; 46 } 47 } 48 for(register auto &j:q[i]) { 49 if(!anc[j.s][0]||anc[j.s][lim]) continue; 50 for(register int i=0;j.k;j.k>>=1,i++) { 51 if(j.k&1) j.s=anc[j.s][i]; 52 } 53 ans[j.id]=j.s; 54 } 55 } 56 for(register int i=0;i<cnt_q;i++) { 57 printf("%d ",ans[i]?:-1); 58 } 59 return 0; 60 }