CodeForces 1945H GCD is Greater

洛谷传送门

CF 传送门

感觉是这场唯一比较有趣的题?

首先明确一点:先手只会选 \(2\) 个数,因为数多了 \(\gcd\) 会变小,而且对方的 \(\text{and}\) 会变大。

所以对于某一位,若 \(0\) 的个数 \(\ge 3\) 那么对方的按位与这一位一定是 \(0\)

所以若 \(0\) 的个数 \(\le 2\),那么可能会选这一位是 \(0\) 的,导致对方的按位与这一位是 \(1\)。把它们加入一个集合 \(S\) 中。

对每一位做这一步后 \(S\) 的大小为 \(O(\log V)\)。枚举 \(S\) 中的每个元素,再 \(O(n)\) 枚举另外一个选什么,\(O(\log V)\) check 一下即可(\(O(\log V)\) 是因为要算 \(\gcd\)。还要写一个 ST 表查询区间按位与)。

\(T = \{1, 2, \ldots, n\} \setminus S\)。此时还没有考虑选 \(T\) 中的数。但是因为选 \(T\) 中的任意两个数,剩下的数的按位与都等于原序列的按位与,所以我们只可能选 \(T\) 中两两 \(\gcd\) 最大的两个数,设它们为 \(a_x, a_y\)。特殊考虑一下它们即可。

总时间复杂度 \(O(n (\log n + \log^2 V))\)

code
// Problem: H. GCD is Greater
// Contest: Codeforces - Codeforces Round 935 (Div. 3)
// URL: https://codeforces.com/contest/1945/problem/H
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 400100;
const int N = 400000;
const int logn = 21;

int n, m, a[maxn], K, b[maxn], f[logn][maxn];
bool vis[maxn];
vector<int> di[maxn];

inline void init() {
	for (int i = 1; i <= N; ++i) {
		for (int j = i; j <= N; j += i) {
			di[j].pb(i);
		}
	}
}

inline int qand(int l, int r) {
	if (l > r) {
		return -1;
	}
	int k = __lg(r - l + 1);
	return (f[k][l] & f[k][r - (1 << k) + 1]);
}

inline int query(int x, int y) {
	if (x > y) {
		swap(x, y);
	}
	return qand(1, x - 1) & qand(x + 1, y - 1) & qand(y + 1, n);
}

void solve() {
	scanf("%d%d", &n, &m);
	K = 0;
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		K = max(K, a[i]);
		vis[i] = 0;
		f[0][i] = a[i];
	}
	for (int j = 1; (1 << j) <= n; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
			f[j][i] = (f[j - 1][i] & f[j - 1][i + (1 << (j - 1))]);
		}
	}
	for (int i = 1; i <= K; ++i) {
		b[i] = 0;
	}
	vector<int> S;
	for (int i = 18; ~i; --i) {
		vector<int> T;
		for (int j = 1; j <= n; ++j) {
			if ((~a[j]) & (1 << i)) {
				T.pb(j);
			}
		}
		if ((int)T.size() <= 2) {
			for (int i : T) {
				S.pb(i);
				vis[i] = 1;
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (!vis[i]) {
			for (int j : di[a[i]]) {
				++b[j];
			}
		}
	}
	for (int i = K; i; --i) {
		if (b[i] >= 2) {
			vector<int> T;
			for (int j = 1; j <= n && (int)T.size() < 2; ++j) {
				if (a[j] % i == 0 && !vis[j]) {
					T.pb(j);
				}
			}
			int x = __gcd(a[T[0]], a[T[1]]), y = query(T[0], T[1]);
			if (x > y + m) {
				printf("YES\n2 %d %d\n%d ", a[T[0]], a[T[1]], n - 2);
				for (int k = 1; k <= n; ++k) {
					if (k == T[0] || k == T[1]) {
						continue;
					}
					printf("%d ", a[k]);
				}
				putchar('\n');
				return;
			}
			break;
		}
	}
	sort(S.begin(), S.end());
	S.erase(unique(S.begin(), S.end()), S.end());
	for (int i : S) {
		for (int j = 1; j <= n; ++j) {
			if (i == j) {
				continue;
			}
			int x = __gcd(a[i], a[j]), y = query(i, j);
			if (x > y + m) {
				printf("YES\n2 %d %d\n%d ", a[i], a[j], n - 2);
				for (int k = 1; k <= n; ++k) {
					if (i == k || j == k) {
						continue;
					}
					printf("%d ", a[k]);
				}
				putchar('\n');
				return;
			}
		}
	}
	puts("NO");
}

int main() {
	init();
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2024-03-19 20:18  zltzlt  阅读(29)  评论(0编辑  收藏  举报