IV.[GYM102900K]Traveling Merchant
首先,观察到路径一定是一个 ( ho) 形的东西,其中在 ( ho) 的交点之前,一直都是黑白点交替,到了交点处是两个同色点。
于是我们就只保留异色边建一张图,则问题就转变为给你多对同色点,询问有无从 (1) 经过其中一个点到达另一个点的简单路径。
考虑建出异色边图的圆方树。则我们只需以 (1) 为根遍历圆方树,将询问的两个圆点升到它们的父亲——两个方点,然后判断它们是否成祖孙关系即可。
时间复杂度 (O(n))。
代码:
#include<bits/stdc++.h>
using namespace std;
int T,n,m,c;
char s[200100];
namespace Tree{
vector<int>v[400100];
void ae(int x,int y){v[x].push_back(y),v[y].push_back(x);}
int dfn[400100],sz[400100],tot,fa[400100];
void dfs(int x,int P){
fa[x]=P,dfn[x]=++tot,sz[x]=1;
for(auto y:v[x])if(y!=P)dfs(y,x),sz[x]+=sz[y];
}
}
namespace Graph{
vector<int>v[200100];
int dfn[200100],low[200100],tot;
stack<int>s;
void Tarjan(int x){
dfn[x]=low[x]=++tot,s.push(x);
for(auto y:v[x]){
if(!dfn[y]){
Tarjan(y),low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
c++;
while(s.top()!=y)Tree::ae(c,s.top()),s.pop();
Tree::ae(c,s.top()),s.pop();
Tree::ae(c,x);
}
}else low[x]=min(low[x],dfn[y]);
}
}
}
vector<pair<int,int> >q;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%s",&n,&m,s+1),c=n;
for(int i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y),x++,y++;
if(s[x]!=s[y])Graph::v[x].push_back(y),Graph::v[y].push_back(x);
else q.push_back(make_pair(x,y));
}
Graph::Tarjan(1),Tree::dfs(1,0);
bool ok=false;
for(auto i:q){
if(!Graph::dfn[i.first]||!Graph::dfn[i.second])continue;
int x=i.first,y=i.second;
if(Tree::fa[x])x=Tree::fa[x];
if(Tree::fa[y])y=Tree::fa[y];
if(Tree::dfn[x]>Tree::dfn[y])swap(x,y);
if(Tree::dfn[x]+Tree::sz[x]-1>=Tree::dfn[y]){ok=true;break;}
}
puts(ok?"yes":"no");
for(int i=1;i<=n;i++)Graph::dfn[i]=Graph::low[i]=0,Graph::v[i].clear();Graph::tot=0;
for(int i=1;i<=c;i++)Tree::dfn[i]=Tree::sz[i]=Tree::fa[i]=0,Tree::v[i].clear();c=0;
while(!Graph::s.empty())Graph::s.pop();
q.clear();
}
return 0;
}