题解:P14799 [JOI 2026 二次预选] 比太郎之旅 3 / Bitaro's Travel 3
题目传送门
题意简述
给定一个 无向简单图。
一条路径合法,满足相邻两点有边,且编号大小交替(小→大→小→大,奇数步升、偶数步降),允许重复经过点。
求每个点无论如何都无法通过合法路径到达的点的个数。
思路
显然将问题转化为求可达点的数量会更加好做。
如果每一个节点都跑一遍 dfs,显然是会 TLE 的,所以我们考虑在一次 dfs 中尽可能地多求出一些点的答案。
如图,以 1 号节点为起点走出的合法路径上,每个红色节点都处于相同相位(下一步方向一致)。由于合法性只取决于大小交替,以这些红色节点为起点也会走出结构相同的路径。于是一次 dfs 就能同时求出这一组点的答案。
观察图片中的白色节点,会发现与它们相连的节点全部比它们大,所以这条路径不适用于它们,那么它们就还需要再次 dfs。由于遍历过程中还是有可能重复,我们仍然需要标记它们,只不过要与红色区分。
所以每个点需要分两种状态分别标记,设
时间复杂度
#include<iostream>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
int p,ans[3000005],cnt,vis[3000005][2];
vector<int>g[3000005],vc;
void dfs(int u,int fa,int zt){
vis[u][zt]=p;
if(vis[u][zt^1]!=p)cnt++;
if(zt==0)vc.push_back(u);
for(auto v:g[u]){
if(v==fa)continue;
if(zt==0){
if(v<u||vis[v][1])continue;
dfs(v,u,1);
}else{
if(v>u||vis[v][0])continue;
dfs(v,u,0);
}
}
}
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i=1;i<=n;i++){
if(vis[i][0])continue;
p++;
cnt=0;
vc.clear();
dfs(i,-1,0);
for(int i=0;i<vc.size();i++){
ans[vc[i]]=cnt;
}
}
for(int i=1;i<=n;i++){
cout<<n-ans[i]<<endl;
}
return 0;
}