【題解】Codeforces Round #765 (Div. 2)
阿新 • • 發佈:2022-01-20
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"); } }