24/05/07 图论

\(\color{green}(1)\) CF711D Directed Roads

  • \(n\) 个点和 \(n\) 条边,第 \(i\) 条边从 \(i\) 连到 \(a_i\)(保证 \(i \ne a_i\))。 每条边需要指定一个方向(无向边变为有向边)。问有多少种指定方向的方案使得图中不出现环,答案对 \(10^9 + 7\) 取模。

  • \(n \le 2 \times 10^5\)

显然图构成了若干棵互不干涉的基环树。那么我们对于每一棵基环树单独考虑,相乘即为答案。

若第 \(i\) 棵基环树中有 \(p_i\) 条边在环上,\(q_i\) 条边不在换上。首先显然有 \(\sum p_i + q_i = n\)

考虑定向后出现环的条件,是当前这 \(p_i\) 条边的指向全部相同,即存在 \(2\) 中存在环的方案。所以不出现环的方案数即 \((2^{p_i} - 2) \times 2^{q_i}\)

$\color{blue}\text{Code}$
int n, a[N], id[N], sum;
vector<int> vec;

int vis[N];

void dfs(int u, int t) {
	id[u] = t;
	vis[u] = 1;
	if (!vis[a[u]]) dfs(a[u], t + 1);
	else if (vis[a[u]] == 1) {
		int w = t - id[a[u]] + 1;
		vec.push_back(w);
		sum -= w;
	}
	vis[u] = 2;
}

int fpm(int a, int b) {
	int res = 1;
	while (b) {
		if (b & 1) res = (ll)res * a % P;
		b >>= 1, a = (ll)a * a % P;
	}
	return res;
}

void Luogu_UID_748509() {
	fin >> n;
	for (int i = 1; i <= n; ++ i ) fin >> a[i];
	sum = n;
	
	for (int i = 1; i <= n; ++ i )
		if (!id[i])
			dfs(i, 1);
	
	int res = fpm(2, sum);
	for (int t : vec) res = (ll)res * (fpm(2, t) - 2) % P;
	
	fout << res;
}

\(\color{blue}(2)\) CF85E Guard Towers

  • 在直角坐标系上有 \(n\) 座塔。要求把这些塔分成两组,使得同组内的两座塔的曼哈顿距离的最大值最小,并求出在此前提下求出有多少种分组方案,对 \(10^9 + 7\) 取模。
  • \(n, x_i, y_i \le 5 \times 10^3\)

首先二分答案。然后以平方的复杂度暴力将不满足条件的塔之间连边。此时,若图形成了一张二分图,那么答案可行。这是第一问。

在这张二分图中会呈现若干个连通块,每个连通块显然后两种黑白染色的方案,即将点分成两组的方案。那么答案即 \(2\) 的连通块数量的幂。这是第二问。

$\color{blue}\text{Code}$
int n, a[N], b[N];

struct Gragh {
	vector<int> g[N];
	void clear() { for (int i = 1; i <= n; ++ i ) g[i].clear(); }
	void add(int a, int b) { g[a].push_back(b); }
	int col[N];
	
	int dfs(int u, int c) {
		col[u] = c;
		int cnt = 1;
		for (int v : g[u]) {
			if (!col[v]) {
				int t = dfs(v, 3 - c);
				if (t == -1) return -1;
				cnt += t;
			}
			else if (col[v] == c) return -1;
		}
		return cnt;
	}
	
	int dsu() {
		fill(col + 1, col + n + 1, 0);
		int res = 1;
		for (int i = 1; i <= n; ++ i )
			if (!col[i]) {
				int t = dfs(i, 1);
				if (t == -1) return -1;
				res = res * 2ll % P;
			}
		return res;
	}
}G;

int chk(int mid) {
	G.clear();
	
	for (int i = 1; i <= n; ++ i )
		for (int j = i + 1; j <= n; ++ j )
			if (abs(a[i] - a[j]) + abs(b[i] - b[j]) > mid)
				G.add(i, j), G.add(j, i);
	
	return G.dsu();
}

void Luogu_UID_748509() {
	fin >> n;
	for (int i = 1; i <= n; ++ i ) {
		fin >> a[i] >> b[i];
	}
	
	int l = 0, r = 1e4, res1 = 0, res2 = 0;
	while (l <= r) {
		int mid = l + r >> 1;
		int t = chk(mid);
		if (t == -1) l = mid + 1;
		else r = mid - 1, res1 = mid, res2 = t;
	}
	
	fout << res1 << '\n' << res2 << '\n';
}

\(\color{purple}(3)\) CF732F Tourist Reform

  • 给定一张 \(n\) 个点 \(m\) 条边的简单无向图,你需要给每条边都确定一个方向,使原图变成一个有向图。设 \(R_i\) 为从 \(i\) 号点出发能到达的不同点的数量。最大化所有 \(R_i\) 的最小值。
  • 输出这个最小值以及每条边的定向方案。
  • \(n , m \le 4 \times 10^5\)

首先根据 CF118E 的思路,一定存在一种为一个 dcc(边双连通分量)中所有边定向的方案,使其成为一个 scc(强连通分量)。具体见这里

所以将所有 dcc 缩点,那么原图将成为一颗树,每个点的点权为这个点所代表的 dcc 的点数。接下来最优的构造方案是将点权最大的点作为这棵树的根,然后让其余边都从儿子指向父亲。容易证明这样是正确的。

$\color{blue}\text{Code}$
int n, m;
vector<int> dcc[N];
int dcc_cnt, dep[N];
bool edges[N];
pair<int, int> p[N];

struct Gragh {
	vector<pair<int, int> > g[N];
	void add(int a, int b, int c) {
		g[a].emplace_back(b, c);
		g[b].emplace_back(a, c);
	}
	
	int dfn[N], low[N], ts, stk[N], top;
	bool is_bridge[N];
	
	void Tarjan_dfs(int u, int from) {
    dfn[u] = low[u] = ++ ts;
    stk[ ++ top] = u;
    
    for (auto t : g[u]) {
      int v = t.first, i = t.second;
      if (!dfn[v]) {
        Tarjan_dfs(v, i);
        low[u] = min(low[u], low[v]);
        if (dfn[u] < low[v]) is_bridge[i] = true;
      }
      else if (i != from) low[u] = min(low[u], dfn[v]);
    }
    
    if (dfn[u] == low[u]) {
      ++ dcc_cnt;
      int y;
      do {
        y = stk[top -- ];
        dcc[dcc_cnt].push_back(y);
      } while (y != u);
    }
	}
	
	void Tarjan() {
		Tarjan_dfs(1, -1);
	}
	
	bool st[N], vis[N];
	void dfs(int u) {
		st[u] = true;
		for (auto t : g[u]) {
			int v = t.first, i = t.second;
			if (vis[i]) continue;
			vis[i] = true;
			if (st[v]) {
				edges[i] = true;
			}
			else {
				edges[i] = false;
				dep[v] = dep[u] + 1;
				dfs(v);
			}
		}
	}
}G;

void Luogu_UID_748509() {
	fin >> n >> m;
	for (int i = 1, a, b; i <= m; ++ i ) {
		fin >> a >> b;
		G.add(a, b, i), G.add(b, a, i);
		p[i].first = a, p[i].second = b;
	}
	
	G.Tarjan();
	
	int k = 0;
	for (int i = 1; i <= dcc_cnt; ++ i )
		if (dcc[i].size() > dcc[k].size())
			k = i;
	
	G.dfs(dcc[k].back());
	
	fout << dcc[k].size() << '\n';
	for (int i = 1; i <= m; ++ i ) {
		int a = p[i].first, b = p[i].second;
		if (G.is_bridge[i]) {
			if (dep[a] < dep[b]) swap(a, b);
		}
		else if (edges[i]) {
			if (dep[a] > dep[b]) swap(a, b);
		}
		else {
			if (dep[a] < dep[b]) swap(a, b);
		}
		fout << a << ' ' << b << '\n';
	}
}
posted @ 2024-05-07 22:16  2huk  阅读(4)  评论(0编辑  收藏  举报