P4652 [CEOI2017]One-Way Streets
前言
介绍一种无需缩点而直接使用 dfs 树的办法,跑得也比较快。
题目
P4652 [CEOI2017]One-Way Streets
分析
首先我们可以直接跑出 dfs 树,然后考虑所有的返祖边:
考虑差分来做,因为这时一定会构成环,而环上的边无论朝向哪,环上的点都是可以互相到达的,也就是说,环上点的朝向是无所谓的,也就是都是 \(\text{B}\) 类边,我们直接树上差分打 \(\text{B}\) 类标记即可。
然后考虑题目的限制,我们直接根据限制给出的条件也一样对 dfs 树上打差分标记,分别是“朝上”和“朝下”标记(注意这里是对一条边的儿子处打标记,同时不是 \(\text{L}\) 和 \(\text{R}\) 标记而是“朝向标记”)
然后我们再对整个树 dfs 一遍确认树边的方向:
如果已经有了 \(\text{B}\) 类标记,那么直接把这条边变成 \(\text{B}\) 即可。
对于剩下的情况:
如果有朝上或者朝下的标记,那么就要根据当前边的方向来决定到底是 \(\text{L}\) 还是 \(\text{R}\) 。
如果什么标记都没有,说明这条边也可以随便标方向,直接标上 \(\text{B}\) 即可。
这个做法的复杂度瓶颈在于求 LCA ,也是可以优化到线性的。
这里使用树剖求 LCA ,复杂度 \(O(n\log n)\)
代码
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;bool f=false;char ch=getchar();
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
#define ull unsigned long long
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define dep(i,y,x) for(int i=(y);i>=(x);i--)
const int N=2e5+5,M=2e5+5,MOD=1e9+7;
int n,m,k;
int head[N],to[N<<1],nex[N<<1],from[N],idx,fre[N],dir[N<<1];
bool vis[N],vis1[N],visi[N<<1];
inline void add(int u,int v,int w){nex[++idx]=head[u];from[idx]=u;to[idx]=v;head[u]=idx;dir[idx]=w;return ;}
int dep[N],fa[N],siz[N],son[N],top[N],tag[N][3];
char Edge[N];
void dfs1(int x,int f){
dep[x]=dep[f]+1,fa[x]=f,siz[x]=1;
for(register int i=head[x];i;i=nex[i]){
int y=to[i];
if(siz[y]) continue;fre[y]=i;visi[i]=visi[i+(dir[i]==0?1:-1)]=true;
dfs1(y,x);siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
return ;
}
void dfs2(int x){
if(x==son[fa[x]]) top[x]=top[fa[x]];
else top[x]=x;
if(son[x]) dfs2(son[x]);
for(register int i=head[x];i;i=nex[i]){
int y=to[i];
if(!top[y]) dfs2(y);
}
return ;
}
inline int QueryLca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
inline bool IsTreeEdge(int id){return (fa[to[id]]==from[id])||(to[id]==fa[from[id]]);}
void dfs(int x){
vis1[x]=true;
for(register int i=head[x];i;i=nex[i]){
int y=to[i];
if(vis1[y]) continue;
dfs(y);
tag[x][0]+=tag[y][0],tag[x][1]+=tag[y][1],tag[x][2]+=tag[y][2];
}
if(tag[x][0]) Edge[fre[x]]='B';
else if(tag[x][1]) Edge[fre[x]]=dir[fre[x]]?'R':'L';
else if(tag[x][2]) Edge[fre[x]]=dir[fre[x]]?'L':'R';
else Edge[fre[x]]='B';
return ;
}
signed main(){
read(n),read(m);
for(register int i=1,u,v;i<=m;i++) read(u),read(v),add(u,v,0),add(v,u,1);
for(register int i=1;i<=n;i++) if(!siz[i]) fre[i]=idx+1,dfs1(i,0),dfs2(i);
for(register int i=1,lca;i<=idx;i++) if(!visi[i]) lca=QueryLca(from[i],to[i]),tag[from[i]][0]++,tag[to[i]][0]++,tag[lca][0]-=2,Edge[i]=(dir[i]?'B':'P');
read(k);
for(register int i=1,u,v,lca;i<=k;i++){
read(u),read(v);lca=QueryLca(u,v);
tag[u][1]++,tag[v][2]++,tag[lca][1]--,tag[lca][2]--;
}
for(register int i=1;i<=n;i++) if(!vis1[i]) dfs(i);
for(register int i=1;i<=idx;i++) if(Edge[i]=='B'||Edge[i]=='L'||Edge[i]=='R') putchar(Edge[i]);
return 0;
}
/*
*/