[CEOI2017]One-Way Streets
巧妙的一道题!!!
题目大意
给定一张 \(n\) 个点 \(m\) 条边的无向图,现在想要把这张图定向。
有 \(p\) 个限制条件,每个条件形如 \((x_i,y_i)\),表示在新的有向图当中,\(x_i\) 要能够沿着一些边走到 \(y_i\)。
现在请你求出,每条边的方向是否能够唯一确定。同时请给出这些能够唯一确定的边的方向。
数据保证有解。
题目分析
什么情况下能够对一条边定向呢?首先,这条边一定不存在于一个连通分量中(如果在连通分量中就不能唯一确定方向了);其次,这条边一定是路径 \(x_i\to y_i\) 上的必经边。
于是我们对整个图 \(G\) 进行边双连通分量缩点得到新图 \(G'\),\(G'\) 一定是森林。\(G\) 中 \(x_i\to y_i\) 上的必经边也就是 \(G'\) 中 \(col[x_i]\to col[y_i]\) 上的必经边。
然后就是重点了:怎样才能判断一条必经边的方向呢?
树上差分可以解决问题,但普通的边差分修改方式是 \(tong[u]+1,tong[v]+1,tong[\operatorname{lca(u,v)}]-1,tong[\operatorname{fa(lca(u,v))}]-1\),可在这里绝对是行不通的。我们需要将树上差分改改。
上图:
假设有一个限制条件为 \(x_i=8,y_i=6\)。那么我们看看 \(8\to 6\) 这条路径。首先 \(8\to 1\) 这一段都是从子节点连向父节点的边,而 \(1\to 6\) 这一段都是从父节点连向子节点的边。
既然如此,如果设 \(f=\operatorname{lca}(x_i,y_i)\),则可以分为两段:\(x_i\to f,f\to y_i\)。所以我们将树上差分改为 \(tong[x_i]+1,tong[y_i]-1\) 就没事了。最后计算时采用递归的方式,一个点的权值就是其儿子节点的权值的和。
如果一个点点权为负数,说明该点与父节点的边是从父节点到子节点方向的;如果一个点点权为正数,说明该点与父节点的边是从子节点到父节点方向的;如果点权为 \(0\),说明无法确定。解释上面已经说过。注意,现在我们不关心 \(tong\) 数组具体的值了,只关注其正负情况。
于是对森林中的每一棵树都进行 \(\rm dfs\) 统计答案,然后就没了。
无论如何,这道题一定需要知道:
如果一个点点权为负数,说明该点与父节点的边是从父节点到子节点方向的;如果一个点点权为正数,说明该点与父节点的边是从子节点到父节点方向的;如果点权为 \(0\),说明无法确定。解释上面已经说过。注意,现在我们不关心 \(tong\) 数组具体的值了,只关注其正负情况。
代码
//2022/5/18
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <climits>//need "INT_MAX","INT_MIN"
#include <cstring>//need "memset"
#include <numeric>
#include <algorithm>
#include <stack>
#define enter putchar(10)
#define debug(c,que) std::cerr << #c << " = " << c << que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i = (st);i <= (ed); ++ i) std::cout << arr[i] << w;
#define speed_up() std::ios::sync_with_stdio(false),std::cin.tie(0),std::cout.tie(0)
#define mst(a,k) memset(a,k,sizeof(a))
#define stop return(0)
const int mod = 1e9 + 7;
inline int MOD(int x) {
if(x < 0) x += mod;
return x % mod;
}
namespace Newstd {
char buf[1 << 21],*p1 = buf,*p2 = buf;
inline int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin),p1 == p2) ? EOF : *p1 ++;
}
inline int read() {
int ret = 0,f = 0;char ch = getc();
while (!isdigit(ch)) {
if(ch == '-') f = 1;
ch = getc();
}
while (isdigit(ch)) {
ret = (ret << 3) + (ret << 1) + ch - 48;
ch = getc();
}
return f ? -ret : ret;
}
inline void write(int x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace Newstd;
const int N = 1e5 + 5;
struct Graph {
struct Node {
int v,id,nxt;
//id:边在输入中的编号
} gra[N << 1];
int head[N];
int idx = 1;
inline void add(int u,int v,int way) {
gra[++ idx] = (Node){v,way,head[u]},head[u] = idx;
}
} g1,g2;
int dfn[N],low[N],col[N],tong[N];
char res[N];
bool in_stack[N],vis[N];
std::stack<int>st;
int n,m,k,num,cnt;
inline void tarjan(int now,int pre) {
low[now] = dfn[now] = ++ num,in_stack[now] = true;
st.push(now);
for (register int i = g1.head[now];i;i = g1.gra[i].nxt) {
if (i != (pre ^ 1)) {
int v = g1.gra[i].v;
if (!dfn[v]) {
tarjan(v,i);
low[now] = std::min(low[now],low[v]);
} else {
low[now] = std::min(low[now],dfn[v]);
}
}
}
if (dfn[now] == low[now]) {
cnt ++;
int u;
do {
u = st.top();st.pop();
in_stack[u] = false,col[u] = cnt;
} while (u != now);
}
}
inline int Abs(int x) {
return x > 0 ? x : -x;
}
//id:now <-> fa(now) 的编号
//way = 1:now <- fa(now)
//way = -1:now -> fa(now)
inline void dfs(int now,int id,int way) {
vis[now] = true;
for (register int i = g2.head[now];i;i = g2.gra[i].nxt) {
int v = g2.gra[i].v;
if (!vis[v]) {
if (g2.gra[i].id > 0) {
dfs(v,Abs(g2.gra[i].id),1);
} else {
dfs(v,Abs(g2.gra[i].id),-1);
}
tong[now] += tong[v];
}
}
if (tong[now] != 0) {
if (tong[now] * way > 0) {
res[id] = 'L';
} else {
res[id] = 'R';
}
}
}
int main(void) {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
n = read(),m = read();
for (register int i = 1;i <= m; ++ i) {
int u = read(),v = read();
//u -> v 是正数
//v -> u 是负数
g1.add(u,v,i),g1.add(v,u,-i);
}
for (register int i = 1;i <= n; ++ i) {
if (!dfn[i]) {
tarjan(i,-1);
}
}
for (register int i = 1;i <= n; ++ i) {
for (register int j = g1.head[i];j;j = g1.gra[j].nxt) {
int v = g1.gra[j].v;
if (col[i] != col[v]) {
g2.add(col[i],col[v],g1.gra[j].id);
}
}
}
k = read();
while (k --) {
int u = read(),v = read();
tong[col[u]] ++,tong[col[v]] --;
}
for (register int i = 1;i <= m; ++ i) res[i] = 'B';
for (register int i = 1;i <= n; ++ i) {
if (!vis[i]) {
dfs(i,0,0);
}
}
printf("%s\n",res + 1);
return 0;
}