[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;
}
posted @ 2022-05-19 00:03  Coros_Trusds  阅读(47)  评论(0编辑  收藏  举报