首先,很显然的是将问题离线处理,然后每个点做一遍遍历,解答以当前点为起点的问题。
我的第一反应是直接遍历,若遇到环全部退掉,没遍历到的就是-1.
但是后来一想,如果环退出之后当前点可能还能到达其他点,我这样的方法就会有漏洞。
所以我们不得不换一个思路。
我们考虑什么样的点是-1
所以这道题的麻烦之处就在于第二种的点的遍历和判断。
此处,我们引入一个时间戳 v i vi vi
对于一个点 x x x, v i [ x ] vi[x] vi[x]一共有三种情况:
1、 v i [ x ] = = 0 : vi[x] == 0: vi[x]==0:当前点x还未遍历过。
2、 v i [ x ] = = x − 1 : vi[x] == x-1: vi[x]==x−1:当前点x正在遍历,但还没遍历完
3、 v i [ x ] = = x : vi[x] == x: vi[x]==x:当前点x已经遍历过,无需再次遍历。
很显然,有了这个时间戳,判断一个环的条件就是 v i [ y ] = = y − 1 vi[y] == y-1 vi[y]==y−1,就说明遍历到了正在遍历过的点,即环。
用 l o p lop lop表示当前环的数量。
用 c n t [ x ] cnt[x] cnt[x]表示由 x x x引出的环的个数。
一旦碰到上面的情况,我们令 l o p + + , c n t [ y ] + + lop++,cnt[y]++ lop++,cnt[y]++,即产生了一个环。
更新的答案的时候只需要判断 l o p lop lop是否为0即可(我们初始化答案为-1,没有更新到即不满足条件。)
#include
using namespace std;
const int N = 4010;
int n,m,Q;
int len,st;
int lop = 0;
int ans[400010];
vector < int > a[N];
struct Node{
int x,y,k,id;
}q[400010];
int b[N];
vector < int > po[3010][3010];
int vi[N],cnt[N];
void in(int x,int y){
for (int i = 0; i < po[x][y].size(); i++){
int k = q[po[x][y][i]].k;
if (k > len || lop != 0) continue;//不满足条件
ans[po[x][y][i]] = b[k];
}
po[x][y].clear();
}
void dfs(int x){
b[++len] = x;
vi[x] = x-1;
if (po[st][x].size()) in(st,x);//更新答案
for (int i = 0; i < a[x].size(); i++){
int y = a[x][i];
if (vi[y] == 0) dfs(y);//没遍历过,遍历
else if (vi[y] == y-1) lop++ , cnt[y]++;//有环,累加环的个数
}
lop-=cnt[x];//当前点遍历完,减去当前点的环的个数
cnt[x] = 0;
b[len--] = 0;
vi[x] = x;//当前点遍历完
}
int main(){
scanf("%d %d %d",&n,&m,&Q);
for (int i = 1,x,y; i <= m; i++)
scanf("%d %d",&x,&y) , a[x].push_back(y);
for (int i = 1; i <= n; i++) sort(a[i].begin() , a[i].end());//排序
for (int i = 1; i <= Q; i++)
scanf("%d %d %d",&q[i].x,&q[i].y,&q[i].k) , po[q[i].x][q[i].y].push_back(i);//将问题离散
for (int i = 1; i <= Q; i++) ans[i] = -1;
for (int i = 1; i <= n; i++){
memset(vi,0,sizeof vi);
st = i;
len = 0;
dfs(i);
}
for (int i = 1; i <= Q; i++) printf("%d\n",ans[i]);
return 0;
}