【LOJ3274】「JOISC 2020 Day2」变色龙之恋

题目链接

点击打开链接

题目解法

考虑子任务 4 4 的解法。
令一只变色龙 x x 和其性别不同的变色龙集合 S S 会面,得到结果 r e s res ,讨论若干情况可得:
( 1 ) (1) 、若 L L x = x L_{L_x}=x ,当且仅当 S S 集合中存在与 x x 颜色相同的变色龙, r e s < S + 1 res<|S|+1
( 2 ) (2) 、否则,当且仅当 S S 集合中存在与 x x 颜色相同或与 x x 存在喜爱关系的变色龙, r e s < S + 1 res<|S|+1

因此,可以通过不超过 3 L o g 2 N + O ( N ) 3Log_2N+O(N) 次操作确定与每一个 x x 有特殊关系的变色龙集合 S x S_x
S x = 1 S_x=1 ,则可以直接确定与其同色的异性变色龙。
否则, S x S_x 必然为 3 3 ,组织 x x 与其中的每两个元素进行一次会面,会有恰好一次得到的答案为 1 1 ,此时没有参与会面的变色龙是 x x 喜欢的变色龙。由此,可以找出所有单向喜欢的关系,确定答案。

对于原题,难点在于如何分离二分图两侧的点集。
但实际上,子任务 4 4 解法中的 S S 集合并不一定需要满足所有变色龙都与 x x 性别不同,只需要满足 S S 集合内不存在特殊关系。因此,对于新处理的某条变色龙 x x ,将已有的特殊关系建出二分图,并染色,对 x x 和二分图两侧的变色龙集合都进行子任务 4 4 中的过程,即可找到新增的特殊关系。

时间复杂度 O ( N 2 L o g N ) O(N^2LogN) ,操作次数不超过 3 L o g 2 N + O ( N ) 3Log_2N+O(N)

#include "chameleon.h"
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 5;
const int Limit = 15000;
bool vis[MAXN];
bool col[MAXN];
int n, cnt, p[MAXN];
vector <int> a[MAXN];
vector <int> b[MAXN];
int ask(vector <int> a) {
	cnt++;
	return Query(a);
}
int ask(vector <int> a, int x) {
	a.push_back(x), cnt++;
	return Query(a);
}
void erase(vector <int> &a, int x) {
	for (unsigned i = 0; i < a.size(); i++)
		if (a[i] == x) {
			swap(a[i], a[a.size() - 1]);
			a.pop_back();
			return;
		}
}
void findedge(vector <int> st, int pos) {
	for (auto x : a[pos])
		erase(st, x);
	while (ask(st, pos) != st.size() + 1) {
		vector <int> now = st;
		while (now.size() != 1) {
			vector <int> a, b;
			for (unsigned i = 0; i < now.size(); i++)
				if (i & 1) a.push_back(now[i]);
				else b.push_back(now[i]);
			if (ask(a, pos) == a.size() + 1) now = b;
			else now = a;
		}
		int tmp = now.back();
		a[pos].push_back(tmp);
		a[tmp].push_back(pos);
		erase(st, tmp);
	}
}
void dfs(int pos, bool type) {
	col[pos] = type;
	vis[pos] = true;
	for (auto x : a[pos])
		if (!vis[x]) dfs(x, !type);
}
void Solve(int N) {
	n = N;
	for (int i = 1; i <= n * 2; i++) {
		memset(vis, false, sizeof(vis));
		for (int j = 1; j <= i - 1; j++)
			if (!vis[j]) dfs(j, true);
		vector <int> st;
		for (int j = 1; j <= i - 1; j++)
			if (col[j]) st.push_back(j);
		if (st.size() != 0) findedge(st, i);
		st.clear();
		for (int j = 1; j <= i - 1; j++)
			if (!col[j]) st.push_back(j);
		if (st.size() != 0) findedge(st, i);
	}
	for (int i = 1; i <= n * 2; i++)
		assert(a[i].size() == 1 || a[i].size() == 3);
	for (int i = 1; i <= n * 2; i++)
		if (a[i].size() == 3) {
			int x = 0;
			if (ask({i, a[i][0], a[i][1]}) == 1) assert(x == 0), x = a[i][2];
			if (ask({i, a[i][0], a[i][2]}) == 1) assert(x == 0), x = a[i][1];
			if (ask({i, a[i][1], a[i][2]}) == 1) assert(x == 0), x = a[i][0];
			assert(x != 0);
			b[i].push_back(x);
			b[x].push_back(i);
		}
	memset(vis, false, sizeof(vis));
	for (int i = 1; i <= n * 2; i++) {
		for (auto x : b[i])
			erase(a[i], x);
		assert(a[i].size() == 1);
		if (vis[i]) continue;
		int res = a[i].back();
		Answer(i, res);
		vis[i] = vis[res] = true;
	}
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

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