1. 程式人生 > 其它 >【題解】Codeforces Round #765 (Div. 2)

【題解】Codeforces Round #765 (Div. 2)

vp→rk156,罰時有些爆炸,題好像都挺經典的?
小清新的分治題,和經典的括號樹的實現

D

小清新題,在二進位制下從高位往低位考慮:
若 K 該位為 0,自然想到分治:將待選集合按該位為 0 或 1 分為兩組,顯然組間的配對是滿足的,組內繼續考慮下一位;
若 K 該位為 1,注意到這時我們只能在待選集合裡選擇最多兩個數,且這兩個數在該位一個為 0,一個為 1,那麼我們同樣對這個區間按該位的數字劃分成兩部分,遞迴下去找那兩個數就行了(另一個遞迴函式 find)
感覺寫的挺好看的,在下標陣列上操作,用 [l, r] 來表示集合

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int T;
const int MAXN = 300005;
int N, K;
int A[MAXN], B[MAXN], C[MAXN];
int S[MAXN];
void di(int l, int r, int &pl, int &pr, int b) // l, pl, pr, r
{
	pl = l-1, pr = r+1;
	for (int i=l; i<=r; i++)
		if (A[B[i]]&(1<<b)) S[++pl] = B[i]; else S[--pr] = B[i];
	for (int i=l; i<=r; i++) B[i] = S[i];
}
int find(int l1, int r1, int l2, int r2, int b)
{
	if (l1> r1 || l2> r2) return 0;
	if (b< 0) { C[B[l1]] = C[B[l2]] = 1; return 1; }
	int pl1, pr1, pl2, pr2;
	di(l1, r1, pl1, pr1, b);
	di(l2, r2, pl2, pr2, b);
	if (K&(1<<b)) {
		if (find(l1, pl1, pr2, r2, b-1)) return 1;
		if (find(l2, pl2, pr1, r1, b-1)) return 1;
	} else {
		if (pl1>=l1 && r2>=pr2) { C[B[l1]] = C[B[r2]] = 1; return 1; }
		if (r1>=pr1 && pl2>=l2) { C[B[r1]] = C[B[l2]] = 1; return 1; }
		if (find(l1, r1, l2, r2, b-1)) return 1;
	}
	return 0;
}
void cal(int l, int r, int b)
{
	if (l==r || b< 0) {
		for (int i=l; i<=r; i++) C[B[i]] = 1;
		return;
	}
	int pl, pr; di(l, r, pl, pr, b);
	if (K&(1<<b)) { if (!find(l, pl, pr, r, b)) C[B[l]] = 1; }
	else {
		if (l<=pl) cal(l, pl, b-1);
		if (pr<=r) cal(pr, r, b-1);
	}
}
int main()
{
	for (T = 1; T; T--) {
		scanf("%d%d", &N, &K);
		for (int i=1; i<=N; i++) scanf("%d", &A[i]), B[i] = i;
		cal(1, N, 30);
		int ans = 0;
		for (int i=1; i<=N; i++) if (C[i]) ans++;
		if (ans> 1) {
			printf("%d\n", ans);
			for (int i=1; i<=N; i++) if (C[i]) printf("%d ", i);
		} else puts("-1");
	}
}