【LOJ3176】「IOI2019」景點劃分

【題目鏈接】

【思路要點】

  • 不妨令 abca\leq b\leq c ,由 a+b+c=Na+b+c=N ,則有 aN3,bN2,cN3a\leq\frac{N}{3},b\leq\frac{N}{2},c\geq\frac{N}{3}
  • 由於我們只希望有兩個導出子圖連通,因此一定是 a,ba,b 對應的兩個子圖,換言之,我們至多可以浪費 cc 個點。
  • 任取一個圖的搜索樹,考慮找到最深的一個子樹大小 a\geq a 的節點 xx ,那麼 xx 的每個子樹大小均 <a<a
  • xx 子樹大小爲 sizexsize_x ,子樹中刪去 xx 後依然與根節點連通的點數爲 sumsum ,若 Nsizex+sum<aN-size_x+sum<a ,那麼顯然可以判斷問題無解,否則,我們可以按照如下方式給出一組構造:
  • Nsizex+sumbN-size_x+sum\geq b ,則我們要求 xx 所在的連通塊大小爲 aa 。我們可以先將子樹中刪去 xx 後不與根節點連通的點加入連通塊,如果此時點數已經達到 aa ,則構造完成,只需要從根節點 dfsdfs 找到相鄰的 bb 個點即可。注意到 xx 的每個子樹大小均 <a<a ,並且我們至多可以浪費 cc 個點,接下來可以一個一個子樹地往連通塊里加點,直到點數達到 aa ,再從根節點 dfsdfs 找到相鄰的 bb 個點。
  • Nsizex+sum<bN-size_x+sum< b ,由 bN2b\leq\frac{N}{2} ,則 sizexbsize_x\geq b ,那麼我們要求 xx 所在的連通塊大小爲 bb 。按照相同的方法處理,即可從根節點 dfsdfs 找到相鄰的 aa 個點。
  • 時間複雜度 O(N+M)O(N+M)

【代碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
vector <int> a[MAXN];
pair <int, int> lim[5];
bool instack[MAXN];
int n, m, cnt, size[MAXN], ans[MAXN];
int timer, dfn[MAXN], low[MAXN];
void dfs(int pos, int col, bool down) {
	if (cnt == 0 || ans[pos]) return;
	ans[pos] = col, cnt--;
	for (auto x : a[pos])
		if (!down || dfn[x] > dfn[pos]) dfs(x, col, down);
}
void tarjan(int pos, int fa) {
	instack[pos] = true;
	dfn[pos] = low[pos] = ++timer;
	int sum = 0; size[pos] = 1;
	vector <int> sons;
	for (auto x : a[pos])
		if (dfn[x] == 0) {
			tarjan(x, pos);
			sons.push_back(x);
			chkmin(low[pos], low[x]);
			size[pos] += size[x];
			if (low[x] < dfn[pos]) sum += size[x];
		} else if (instack[x]) chkmin(low[pos], dfn[x]);
	if (size[pos] >= lim[1].first) {
		if (n - size[pos] + sum < lim[1].first) {
			for (int i = 1; i <= n; i++)
				printf("%d ", 0);
			printf("\n");
			exit(0);
		}
		if (n - size[pos] + sum < lim[2].first) swap(lim[1], lim[2]);
		int colx = lim[1].second, coly = lim[2].second;
		cnt = lim[1].first, ans[pos] = colx, cnt--;
		for (auto x : sons) if (low[x] >= dfn[pos]) dfs(x, colx, true);
		for (auto x : sons) if (low[x] < dfn[pos]) dfs(x, colx, true);
		assert(cnt == 0);
		cnt = lim[2].first;
		dfs(1, coly, false);
		assert(cnt == 0);
		for (int i = 1; i <= n; i++) {
			if (ans[i] == 0) ans[i] = lim[3].second;
			printf("%d ", ans[i]);
		}
		printf("\n");
		exit(0);
	}
	instack[pos] = false;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= 3; i++) {
		int x; read(x);
		lim[i] = make_pair(x, i);
	}
	sort(lim + 1, lim + 4);
	assert(lim[1].first + lim[2].first + lim[3].first == n);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y), x++, y++;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	tarjan(1, 0);
	assert(false);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章