P8943 Deception Point 题解

Description

题目给的很详细了。

Solution

首先 \(n\) 个点 \(n\) 条边,我们很容易就想到基环树(比正常的树多了一条边,形成了一个环),不会也没关系,这题跟基环树其实关系不大。

首先,我们可以发现题目中说明了这个环不是一个四元及以下的环,这代表着如果 \(A\) 提前进入了这个环,那么他就可以和 \(B\) 周旋,这样 \(B\) 就永远都抓不到 \(A\) ;相反的,如果 \(B\) 赶在 \(A\) 之前就把 \(A\) 通往环内的那个点就封死了,那么 \(A\) 就只能静待被抓了。

想到这里其实思路就很明了了,我们需要预处理出这些东西:

  • 求出这个环;

  • 求出每个不在环上的点到环的距离以及最初到环上的哪个点;

  • 求出环内点与点之间的距离。

由于赛前几天一直在学习边双,降智了,所以找环我用的边双做的(,其实也可以用拓扑等方法来解决;

对于第二个问题,我们 \(O(n)\) 枚举每一条边,如果出现了点 \(x\) 在环内点 \(y\) 不在环内的情况,我们从 \(y\) 开始进行广搜给 \(y\) 延伸出来的子树打上标记。

对于第三个问题,我们随便断掉环上的一条边,这样形成的就是一条链,我们在链上以断的那条边的其中一个端点为起点再进行一次广搜,处理出环上每个点到这个端点的距离,那么最后环上两个点 \(x,y\) 的最短距离就是 \(\min(\lvert len_x-len_y \rvert,size- \lvert len_x-len_y \rvert)\),其中 \(size\) 表示的是这个环的大小。

由此可知,总复杂度是一个线性的,可以通过。

Code

#include<bits/stdc++.h>
//define int long long
#define ll long long
#define next nxt
#define re register
#define il inline
const int N = 4e5 + 5;
using namespace std;
int max(int x,int y){return x > y ? x : y;}
int min(int x,int y){return x < y ? x : y;}

int n,q,u,v,cnt,tot,idx,ans,k,cutx,cuty;
int low[N],dfn[N],stk[N],belong[N],len[N];
struct Node{
	int tp,dep;//tp表示这个不在环上的点通过哪个点进入环,以及到环的距离
}f[N];
vector <int> vec[N];
struct node{
	int u,v,next;
}edge[N<<1]; int head[N],num_edge;

il int read()
{
	int f=0,s=0;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) f |= (ch=='-');
	for(; isdigit(ch);ch=getchar()) s = (s<<1) + (s<<3) + (ch^48);
	return f ? -s : s;
}

il void add(int from,int to)
{
	edge[++num_edge] = (node){from,to,head[from]};
	head[from] = num_edge;
}

il void tarjan(int x,int in_edge)//边双缩点
{
	dfn[x] = low[x] = ++tot;
	stk[++idx] = x;
	for(re int i=head[x];i;i=edge[i].next)
	{
		int y = edge[i].v;
		if(!dfn[y])
		{
			tarjan(y,i);
			low[x] = min(low[x],low[y]);
		}
		else if(i != (in_edge^1)) low[x] = min(low[x],dfn[y]);
	}
	if(dfn[x] == low[x])
	{
		++cnt;
		while(x != stk[idx+1])
		{
			vec[cnt].push_back(stk[idx]);
			belong[stk[idx]] = cnt;
			idx--;
		}
	}
}

il void bfs(int tp,int x,int dep)//求不在环上的点从哪个点入环,路径长度是多少
{
	f[x] = (Node){tp,dep};
	queue <int> q;
	q.push(x);
	while(!q.empty())
	{
		u = q.front(); q.pop();
		for(re int i=head[u];i;i=edge[i].next)
		{
			v = edge[i].v;
			if(v == tp) continue;
			if(!f[v].dep)
			{
				f[v].dep = f[u].dep + 1 , f[v].tp = tp;
				q.push(v);
			}
		}
	}
}

il void circle()//求断边后链上路径长
{
	memset(len , -1 , sizeof len);
	len[cutx] = 0;
	queue <int> q;
	q.push(cutx);
	while(!q.empty())
	{
		u = q.front(); q.pop();
		for(re int i=head[u];i;i=edge[i].next)
		{
			v = edge[i].v;
			if((u == cutx && v == cuty) || (v == cutx && u == cuty)) continue;
			if(belong[v] != k) continue;
			if(len[v] == -1)
			{
				len[v] = len[u] + 1;
				q.push(v);
			}
		}
	}
}

signed main()
{
	n = read() , q = read();
	num_edge = 1;
	for(re int i=1;i<=n;i++)
	{
		u = read() , v = read();
		add(u,v) , add(v,u);
	}
	tarjan(1,1);//边双缩点找环
	for(re int i=1;i<=cnt;i++) if(vec[i].size() > 1) { k = i; break; }//找哪个是环,步骤一
	for(re int i=2;i<=num_edge;i++)
	{
		u = edge[i].u , v = edge[i].v;
		if(belong[u] == belong[v]) { cutx = u , cuty = v ; continue; }//随便找一条在环上的边
		if(belong[u] == k && belong[v] != k) bfs(u,v,1);//一个在环上一个不在环上,进行步骤二
	}
	circle();//步骤三
	int siz = vec[k].size() , disu , disv;
	while(q--)
	{
		u = read() , v = read();
		if(belong[u] == k) { cout << "Survive" << "\n"; continue; }//原本就在环里那直接跑了
		disu = f[u].dep;//计算路径长度
		if(belong[v] == k) disv = min(abs(len[f[u].tp]-len[v]),siz-abs(len[f[u].tp]-len[v]));//B要拦截的距离就是B到环的距离B的tp和A的tp之间的距离
		else disv = f[v].dep + min(abs(len[f[u].tp]-len[f[v].tp]),siz-abs(len[f[u].tp]-len[f[v].tp]));
		if(disu < disv) cout << "Survive" << "\n";
		else cout << "Deception" << "\n";
	}
	return 0;
}
posted @ 2023-05-25 21:51  Bloodstalk  阅读(15)  评论(0编辑  收藏  举报