P4652 [CEOI 2017] One-Way Streets
题目传送门
tarjan 求桥,缩点,数上差分,LCA,DSU。
题目大意
给定一张 \(n\) 个点 \(m\) 条边的无向图,可能有重边,现在想要把这张图定向。
有 \(p\) 个限制条件,每个条件形如 \((x_i,y_i)\) ,表示在新的有向图当中, \(x_i\) 要能够沿着一些边走到 \(y_i\)。
现在请你求出,每条边的方向是否能够唯一确定。同时请给出这些能够唯一确定的边的方向。
\((1\leq n,m,p\leq 10^5)\)
题解
转化题意。
先不考虑无解的情况,注意无解应该要归结到无法确定方向这一类上。
一条边能确定方向,当且仅当某个限制条件中的点对必须经过该边,那么这条边的方向就确定了。
必须经过这个条件可以转化为两个约束:
- 该边在限制点对的路径上。
- 该边是桥,即割掉该边后图会被断开。
这个条件在对在环上的边显然永远不成立,也就是说在环上,也就是边双连通分量上的边一定是无法确定方向的。
换句话说,只有桥可能成为答案
那么我们可以先进行缩点(这样最直观,但不代表一定要缩点),这样整张图就变成了一个森林(注意原本的图不一定连通),这样每一条边都是原图的桥了,并且每一条限制只会产生一条路径,于是题意转化为:
一片森林被多条路径覆盖,求所有被覆盖的点
我们发现不好考虑每一条边是否被覆盖,显然好考虑的是每一条路径对答案的贡献,但是暴力覆盖一定是超时的。
我们显然可以进行优化,将一段路径拆成两端 \(a-lca(a,b)\) 和 \(lca(a,b)-b\) ,对于单独的一条笔直的路径,显然是可以差分的,我们只需要在开头和结尾差分,最后对每棵树做一次前缀和即可,容易实现。
注意这里的路径是有方向的,向上和向下,要分两个数组分别维护。
现在考虑什么时候是无解的情况,显然当一个边被两个方向都覆盖了,那么它就无解了,这里枚举每条边看一下就好了。
完结撒花。
代码会很长很长,但是我这个思路应该是最无脑的了把。
代码
#include <bits/stdc++.h>
#define eb push_back
using namespace std;
const int N=1e5+5;
using PII = pair<int,int>;
namespace dsu{
int fa[N];
void init(int n){
for(int i=1;i<=n;i++) fa[i]=i;
}
int find(int x){
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return ;
fa[fy]=fx;
}
bool same(int x,int y){
return find(x)==find(y);
}
};
int n,m,p;
int xx[N],yy[N],ago[N],ans[N];
struct EdgeNode {
int to, id;
};
vector<EdgeNode> e[N];
vector<int> E[N],rt,G[N];
struct Edge{
int x,y;
bool z;
int id;
bool operator < (const Edge &b){
if(x!=b.x) return x<b.x;
return y<b.y;
}
};
vector<Edge> edge;
pair<bool,int> find(int u,int v){
int l=0,r=edge.size()-1,res=-1;
while(l<=r){
int mid=l+r>>1;
if(edge[mid].x==u){
if(edge[mid].y==v){
res=mid;
break;
}
if(edge[mid].y<v) l=mid+1;
else r=mid-1;
}
else{
if(edge[mid].x<u) l=mid+1;
else r=mid-1;
}
}
if(res==-1){
cout<<"ERR\n";
return make_pair(false,1);
}
return make_pair(edge[res].z,edge[res].id);
}
int dfn[N],low[N],Time;
int bel[N],ebcc_cnt;
vector<PII> br;
vector<bool> is_bridge;
void tarjan(int u, int in_edge) {
dfn[u] = low[u] = ++Time;
for (auto [v, id] : e[u]) {
if (id == in_edge) continue;
if (!dfn[v]) {
tarjan(v, id);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) {
br.eb({u, v});
is_bridge[id] = true;
}
} else {
low[u] = min(low[u], dfn[v]);
}
}
}
void dfs_ebcc(int u, int cnt) {
bel[u] = cnt;
for (auto [v, id] : e[u]) {
if (!bel[v] && !is_bridge[id])
dfs_ebcc(v, cnt);
}
}
void build(){
Time = 0;
is_bridge.resize(m+1, false);
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i, 0);
ebcc_cnt = 0;
for(int i=1;i<=n;i++)
if(!bel[i]) dfs_ebcc(i, ++ebcc_cnt);
for(auto [u,v] : br){
int bu = bel[u], bv = bel[v];
E[bu].eb(bv);
E[bv].eb(bu);
dsu::merge(bu, bv);
}
}
bool vis1[N];
int dep[N],fa[N];
void dfs1(int u){
vis1[u]=true;
for(int v:E[u]){
if(vis1[v]) continue;
dep[v]=dep[u]+1;
fa[v]=u;
dfs1(v);
}
}
int f[N][20];
void pre(){
for(int i=1;i<=ebcc_cnt;i++) f[i][0]=fa[i];
for(int j=1;j<=17;j++)
for(int i=1;i<=ebcc_cnt;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=17;i>=0;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=17;i>=0;i--)
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
return fa[x];
}
int col1[N],col2[N];
void draw(int u){
for(int v:E[u]){
if(v==fa[u]) continue;
draw(v);
col1[u]+=col1[v];
col2[u]+=col2[v];
}
}
int main(){
freopen("oneway.in","r",stdin);
freopen("oneway.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back({v, i});
e[v].push_back({u, i});
edge.push_back({u, v, true, i});
edge.push_back({v, u, false, i});
}
sort(edge.begin(),edge.end());
cin>>p;
for(int i=1;i<=p;i++)
cin>>xx[i]>>yy[i];
dsu::init(n);
build();
for(int i=1;i<=ebcc_cnt;i++)
if(!vis1[i]){
rt.eb(i);
dfs1(i);
}
pre();
for(int i=1;i<=p;i++){
int X=bel[xx[i]], Y=bel[yy[i]];
if(X==Y) continue;
if(!dsu::same(X,Y)) continue;
col1[X]++;
col2[Y]++;
int L=lca(X,Y);
col1[L]--;
col2[L]--;
}
for(int it:rt) draw(it);
for(auto [u,v]:br){
int U=bel[u], V=bel[v];
if(dep[U]>=dep[V]) swap(U,V),swap(u,v);
int tmp=0;
if(col1[V]>0 && col2[V]>0) tmp=0;
else if(col1[V]>0) tmp=1;
else if(col2[V]>0) tmp=-1;
auto [f,Id]=find(u,v);
if(f) ans[Id] = -tmp;
else ans[Id] = tmp;
}
for(int i=1;i<=m;i++)
if(ans[i]==1) cout<<'R';
else if(ans[i]==-1) cout<<'L';
else cout<<'B';
return 0;
}
本文来自博客园,作者:_AzureSky,转载请注明原文链接:https://www.cnblogs.com/-AzureSky-/p/19098370

浙公网安备 33010602011771号