T1:
给你一个长度为n的序列a,求一个最小n的排列p,使 max i = 1 n − 1 ( a [ p [ i ] ] x o r a [ p [ i + 1 ] ] ) \max_{i=1}^{n-1}(a[p[i]]~xor~a[p[i+1]]) maxi=1n−1(a[p[i]] xor a[p[i+1]])最小。
题解:
不知道为什么想了这么久就是不觉得这题是个贪心。
按照位运算贪心的基本思想,肯定是从高位到低位。
仔细想想不难发现,假设在一位上既有0又有1,那么最优的一种排列方式一定在把0放一边,把1放一边,要求中间的异或和最小,这样就可以求出答案,套路就是用trie。
然后就是字典序最小。
考虑现在把数分成了两个集合,定义边为能接到下一个(双向),两个集合自己是完全图,中间有一些边,中间的边的端点的异或和一定等于ans, 不然就不满足刚才所说。
很容易想到直接贪心的取,判断一个点是否能取的条件是:
一个点x不能取,则x所在点集大小大于1,且另一点集大小大于1,中间的所有边都经过x。
这样下一个可能的决策点也就出来,这个点集的最小和次小,另一点集相邻的最小。
那么用一堆STL维护就可以了,还挺好写的。
T2:
给出长度为n的序列a,求a的最长的异或和为0的子序列。
1<=n,a<=500000
题解:
直接做发现什么性质都没有。
不如补集转换,变成选最少的数,使异或和为S(S=所有数的异或和),
这个就能直接二分,因为如果x个数可行,x+2个数一定可行,当然会TLE
异或可以联想到线性基,不难得到现在的答案<=log n,因为把整个序列建线性基,只用log n个基底就一定可以表示目前所有数的异或空间。
然后就显然了,用FWT优化,不过FWT求一个点的值可以做到O(n),这个后面博客要补。
复杂度
O
(
n
l
o
g
n
)
O(n~log~n)
O(n log n)
Code(T1):
#include<stdio.h>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#define pp printf
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define ins insert
using namespace std;
const int N = 3e5 + 5;
int n, a[N];
const int w = 29;
int a2[w + 1], cw[w + 1];
int son[N * (w + 1)][2], tot;
void add(int x) {
int p = 0;
fd(i, w, 0) {
int c = (x & a2[i]) > 0;
if(!son[p][c]) son[p][c] = ++ tot;
p = son[p][c];
}
}
int ff(int x) {
int p = 0, s = 0;
fd(i, w, 0) {
int c = (x & a2[i]) > 0;
if(son[p][c]) p = son[p][c]; else
p = son[p][!c], s += a2[i];
}
return s;
}
int g, ans;
set<int> b[2];
int pd(int x) {
return (a[x] & a2[g]) > 0;
}
map<int, int> c[2];
int siz[2];
ll bs;
int pdq(int x) {
return siz[pd(x)] > 1 && bs >= 1 && c[!pd(x)][ans ^ a[x]] == bs;
}
map<int, vector<int> > t[2];
void del(int x) {
bs -= c[!pd(x)][ans ^ a[x]];
c[pd(x)][a[x]] --;
siz[pd(x)] --;
b[pd(x)].erase(x);
t[pd(x)][a[x]].pop_back();
}
int d[N];
int main() {
a2[0] = 1; fo(i, 1, w) a2[i] = a2[i - 1] * 2;
scanf("%d", &n);
fo(i, 1, n) scanf("%d", &a[i]);
fo(i, 1, n) {
fo(j, 0, w) cw[j] += (a[i] & a2[j]) > 0;
}
g = -1;
fd(i, w, 0) if(cw[i] != 0 && cw[i] != n) {
g = i; break;
}
if(g == -1) {
fo(i, 1, n) pp("%d ", i);
pp("\n");
return 0;
}
fo(i, 1, n) if(pd(i))
add(a[i]), b[1].insert(i), c[1][a[i]] ++, siz[1] ++;
ans = 2e9;
fo(i, 1, n) if(!pd(i))
ans = min(ans, ff(a[i])), b[0].insert(i), c[0][a[i]] ++, siz[0] ++;
fo(i, 1, n) if(pd(i))
bs += c[0][ans ^ a[i]];
fd(i, n, 1) t[pd(i)][a[i]].push_back(i);
int no = 1;
while(pdq(no)) no ++;
fo(ii, 1, n) {
d[ii] = no;
del(no);
if(ii == n) continue;
int y = pd(no);
int mx = n + 1, cm = n + 1, dm = n + 1;
if(b[y].size()) {
mx = *b[y].begin();
if(pdq(mx)) mx = n + 1;
}
if(b[y].size() > 1) {
cm = *(++ b[y].begin());
if(pdq(cm)) cm = n + 1;
}
if(t[!y][ans ^ a[no]].size()) {
dm = t[!y][ans ^ a[no]][t[!y][ans ^ a[no]].size() - 1];
if(pdq(dm)) dm = n + 1;
}
int nno = min(mx, min(cm, dm));
no = min(mx, min(cm, dm));
}
fo(i, 1, n) pp("%d ", d[i]);
pp("\n");
}
Code(T2):
#include<cstdio>
#define low(a) ((a) & -(a))
#define pp printf
#define ul unsigned long long
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x; i < y; i ++)
using namespace std;
const int N = 5e5 + 5;
int n, a[N], s;
const int M = 524288;
int b[M]; int c1[M];
int tp;
ul c[M];
void dft(int *a, int tp) {
int n = 1 << tp;
for(int h = 1; h < n; h *= 2)
for(int j = 0; j < n; j += 2 * h) {
int A, *l = a + j, *r = a + j + h;
ff(i, 0, h) A = *r, *r = *l - A, *l = *l + A, l ++, r ++;
}
}
int main() {
scanf("%d", &n) ;
fo(i, 1, n) scanf("%d", &a[i]), s ^= a[i];
if(s == 0) {
pp("%d\n", n); return 0;
}
tp = 19; fo(i, 1, n) b[a[i]] ++;
dft(b, tp);
ff(i, 1, 1 << tp) c1[i] = c1[i - low(i)] + 1;
ff(i, 0, 1 << tp) c[i] = 1;
for(int ans = 1; ans <= 19; ans ++) {
ff(i, 0, 1 << tp) c[i] = c[i] * b[i];
ul sum = 0; ff(i, 0, 1 << tp) sum += c[i] * (c1[i & s] & 1 ? -1 : 1);
if(sum) {
pp("%d\n", n - ans);
return 0;
}
}
pp("0\n");
}