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;
}
/*
*/
posted @ 2021-09-12 22:40  __Anchor  阅读(63)  评论(0)    收藏  举报