【LOJ3280】「JOISC 2020 Day4」首都城市

题目链接

点击打开链接

题目解法

考虑对各个颜色建立满足如下性质的图 G G
若颜色 i i 形成的虚树内存在颜色 j j ,连边 i j i\rightarrow j

若能够得到 G G ,则运行 Tarjan 算法,找到出度为零的所有强连通分量,取最优即可。
在树上倍增优化建图,可以得到具有同样性质的图。

时间复杂度 O ( N L o g N ) O(NLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXLOG = 20;
const int MAXP = 5e6 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int n, k, tot, depth[MAXN], timer, dfn[MAXN];
int point[MAXN][MAXLOG], father[MAXN][MAXLOG];
vector <int> t[MAXN], p[MAXN], a[MAXP];
namespace Tarjan {
	int scc, belong[MAXP];
	int timer, dfn[MAXP], low[MAXP];
	int top, Stack[MAXP];
	bool instack[MAXP];
	void tarjan(int pos) {
		dfn[pos] = low[pos] = ++timer;
		instack[pos] = true;
		Stack[++top] = pos;
		for (unsigned i = 0; i < a[pos].size(); i++)
			if (dfn[a[pos][i]] == 0) {
				tarjan(a[pos][i]);
				low[pos] = min(low[pos], low[a[pos][i]]);
			} else if (instack[a[pos][i]]) low[pos] = min(low[pos], dfn[a[pos][i]]);
		if (low[pos] == dfn[pos]) {
			int tmp = Stack[top--];
			instack[tmp] = false;
			belong[tmp] = ++scc;
			while (tmp != pos) {
				tmp = Stack[top--];
				instack[tmp] = false;
				belong[tmp] = scc;
			}
		}
	}
	int work() {
		static int cnt[MAXP];
		static bool outd[MAXP];
		for (int i = 1; i <= tot; i++)
			if (dfn[i] == 0) tarjan(i);
		for (int i = 1; i <= tot; i++)
		for (auto x : a[i]) if (belong[x] != belong[i]) outd[belong[i]] = true;
		for (int i = 1; i <= k; i++)
			cnt[belong[i]]++;
		int ans = k;
		for (int i = 1; i <= scc; i++)
			if (!outd[i]) chkmin(ans, cnt[i] - 1);
		return ans;
	}
}
int lca(int x, int y) {
	if (depth[x] < depth[y]) swap(x, y);
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (depth[father[x][i]] >= depth[y]) x = father[x][i];
	if (x == y) return x;
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (father[x][i] != father[y][i]) {
			x = father[x][i];
			y = father[y][i];
		}
	return father[x][0];
}
void dfs(int pos, int fa) {
	dfn[pos] = ++timer;
	father[pos][0] = fa;
	point[pos][0] = ++tot;
	depth[pos] = depth[fa] + 1;
	for (int i = 1; (1 << i) <= depth[pos]; i++) {
		father[pos][i] = father[father[pos][i - 1]][i - 1];
		point[pos][i] = ++tot;
		a[tot].push_back(point[pos][i - 1]);
		a[tot].push_back(point[father[pos][i - 1]][i - 1]);
	}
	for (auto x : t[pos])
		if (x != fa) dfs(x, pos);
}
void addedge(int from, int x, int y) {
	int z = lca(x, y);
	for (int i = MAXLOG - 1; i >= 0; i--) {
		if (depth[father[x][i]] >= depth[z]) {
			a[from].push_back(point[x][i]);
			x = father[x][i];
		}
		if (depth[father[y][i]] >= depth[z]) {
			a[from].push_back(point[y][i]);
			y = father[y][i];
		}
	}
	a[from].push_back(point[z][0]);
}
int main() {
	read(n), read(k), tot = k;
	for (int i = 1; i <= n - 1; i++) {
		int x, y; read(x), read(y);
		t[x].push_back(y);
		t[y].push_back(x);
	}
	dfs(1, 0);
	for (int i = 1; i <= n; i++) {
		int x; read(x);
		p[x].push_back(i);
		a[point[i][0]].push_back(x);
	}
	for (int i = 1; i <= k; i++) {
		sort(p[i].begin(), p[i].end(), [&] (int x, int y) {return dfn[x] < dfn[y]; });
		if (p[i].size()) {
			p[i].push_back(p[i][0]);
			for (unsigned j = 1; j < p[i].size(); j++)
				addedge(i, p[i][j - 1], p[i][j]);
		}
	}
	cout << Tarjan :: work() << endl;
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/105074617