【题解 && 巧妙的判环】 Cities Excursions

10 篇文章 0 订阅
题目传送门

题目描述:

在这里插入图片描述


Solution

首先,很显然的是将问题离线处理,然后每个点做一遍遍历,解答以当前点为起点的问题。

我的第一反应是直接遍历,若遇到环全部退掉,没遍历到的就是-1.
但是后来一想,如果环退出之后当前点可能还能到达其他点,我这样的方法就会有漏洞。

所以我们不得不换一个思路。
我们考虑什么样的点是-1

  • 第一种,就是到达当前点的点数小于 K K K,毫无疑问不行。
  • 第二种,就是当前遍历到了一个环,由环上的点遍历出去的点都是-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]==x1当前点x正在遍历,但还没遍历完
3、 v i [ x ] = = x : vi[x] == x: vi[x]==x当前点x已经遍历过,无需再次遍历。

很显然,有了这个时间戳,判断一个环的条件就是 v i [ y ] = = y − 1 vi[y] == y-1 vi[y]==y1,就说明遍历到了正在遍历过的点,即环。

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,没有更新到即不满足条件。)


Code

#include<bits/stdc++.h>
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值