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

題目鏈接

點擊打開鏈接

題目解法

考慮對各個顏色建立滿足如下性質的圖 GG
若顏色 ii 形成的虛樹內存在顏色 jj ,連邊 iji\rightarrow j

若能夠得到 GG ,則運行 Tarjan 算法,找到出度爲零的所有強連通分量,取最優即可。
在樹上倍增優化建圖,可以得到具有同樣性質的圖。

時間複雜度 O(NLogN)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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章