有一个数组
a
a
a,其中有
n
n
n个元素,现在请构造一个数组
b
b
b,其中也有
n
n
n个元素,使得数组
b
b
b中所有元素的异或值为
0
0
0,且
a
i
a_i
ai到
b
i
b_i
bi的距离之和最小。
T
<
=
6
,
n
<
=
15
,
a
i
<
=
1
e
9
T<=6 , n <=15 ,a_i<=1e9
T<=6,n<=15,ai<=1e9
这个。
从高位到低位
D
P
DP
DP。
当所有数在该位的异或和为
0
0
0,可以直接看下一位。
否则,显然最优决策一定是把一个在该位(第
i
i
i位,
2
i
2^i
2i)为
1
1
1的数变为
2
i
−
1
2^i-1
2i−1
或者把一个在该位为
0
0
0的数变为
2
i
2^i
2i。
那么这就是一个
O
(
n
)
O(n)
O(n)的转移过程。
这些变化我们可以用
3
n
3^n
3n的三进制状态表示。
复杂度为
O
(
T
w
n
3
n
)
O(Twn3^n)
O(Twn3n),其中
w
w
w为最高位数。
显然过不了。
可以用队列拓展来刷表转移。
但是状态数还是很多。
我们可以玄学。
如果我们当前要拓展的状态,和他同一层的最小值
<
<
<当前状态
D
P
DP
DP值
−
n
∗
2
i
+
1
-n*2^{i+1}
−n∗2i+1,那么我们是可以不拓展当前状态的,因为不可能追得上。
然后跑的比
s
t
d
std
std还快。
s
t
d
std
std是发现变为
2
i
2^i
2i和变为
2
i
−
1
2^i-1
2i−1有相似点可以用
2
n
2^n
2n的状态存储并加一维维护异或和。
O
(
T
w
n
2
n
)
O(Twn2^n)
O(Twn2n)
A
C
C
o
d
e
s
\rm AC \ Codes
AC Codes
O
(
T
w
n
3
n
)
O(Twn3^n)
O(Twn3n)
#include<bits/stdc++.h>
#define maxn 15
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
int n,a[maxn],pw[maxn]={1};
LL f[2][14348908],sum=0,pmn[2];
int q[2][14348908],R[2];
int b[maxn],c[maxn],now,pre,mx;
void check(int u,LL c){
if(f[now][u] == inf) q[now][R[now]++] = u;
f[now][u] = min(f[now][u] , c);
pmn[now] = min(pmn[now] , c);
}
int main(){
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
int T;
for(int i=1;i<maxn;i++) pw[i] = pw[i-1] * 3;
memset(f,0x3f,sizeof f);
for(scanf("%d",&T);T--;){
scanf("%d",&n);
mx = 0;
for(int i=0;i<n;i++) scanf("%d",&a[i]),mx=max(mx,a[i]);
now=1,pre=0;
f[now][0]=0;
q[now][R[now]++]=0;
pmn[now] = pmn[pre] = inf;
int MX = 0;
for(int i=30;i>=0;i--){
bool flg = 0;
if((1<<i)>mx) continue;
swap(now,pre);
for(int u;R[pre];){
u=q[pre][--R[pre]];
if(f[pre][u] < pmn[pre] + n * (2ll<<i)){
flg = 0;
for(int j=0,v=u;j<n;j++,v/=3){
c[j]=b[j]=v%3;
if(b[j] == 0) flg ^= (a[j]>>i&1);
else if(b[j] == 1) flg ^= 1;
}
if(flg){
for(int j=0;j<n;j++){
int cst=0;
if(b[j]==0){
if(a[j]>>i&1) c[j]=1,cst=a[j]-(1<<i)+1;
else c[j]=2,cst=(1<<i)-a[j];
int nsta = u + (c[j]-b[j]) * pw[j];
check(nsta,f[pre][u]+cst);
}
}
if(u) check(u,f[pre][u]+(1<<i));
}
else{
check(u,f[pre][u]);
}
}
f[pre][u]=inf;
}
MX = max(MX , R[now]);
pmn[pre] = inf;
for(int j=0;j<n;j++) a[j] %= (1<<i);
}
printf("%d\n",MX);
LL ans = inf;
for(;R[now];){
ans = min(ans , f[now][q[now][R[now]-1]]);
f[now][q[now][--R[now]]]=inf;
}
printf("%lld\n",ans);
}
}
O ( T w n 2 n ) 的 s t d O(Twn2^n)的std O(Twn2n)的std
#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i, x, y) for (int i = (x), _ = (y); i <= _; ++i)
#define down(i, x, y) for (int i = (x), _ = (y); i >= _; --i)
#define x first
#define y second
#define mp make_pair
#define pb push_back
#define bin(x) (1<<(x))
//#define LX_JUDGE
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
template<typename T> inline void upmax(T &x, T y) { x < y ? x = y : 0; }
template<typename T> inline void upmin(T &x, T y) { x > y ? x = y : 0; }
template<typename T>
inline void read(T &x) {
char c;
while ((c = getchar()) < '0' || c > '9');
for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0');
}
const int inf = 0x3f3f3f3f;
const int maxn = 15;
int dp[2][bin(maxn) + 1][2][2];
int a[maxn], n;
void Solve() {
read(n);
rep (i, 0, n - 1) {
read(a[i]);
}
memset(dp[0], 0x3f, sizeof(dp[0]));
dp[0][0][0][0] = 0;
int fr = 0, to = 1;
down (s, 30, 0) {
rep (i, 0, n - 1) {
int v = (a[i] >> s) & 1;
int cost = a[i] & (bin(s) - 1);
cost = !v ? bin(s) - cost : cost + 1;
memset(dp[to], 0x3f, sizeof(dp[to]));
rep (j, 0, bin(n) - 1) {
rep (l, 0, 1) rep (o, 0, 1) {
int x = dp[fr][j][l][o];
if (j & bin(i)) {
upmin(dp[to][j][l][o], x);
upmin(dp[to][j][l ^ 1][o], x + bin(s));
} else {
upmin(dp[to][j][l ^ v][o], x);
upmin(dp[to][j | bin(i)][l ^ v ^ 1][o ^ v], x + cost);
}
}
}
swap(fr, to);
}
rep (j, 0, bin(n) - 1) {
dp[fr][j][1][0] = inf;
dp[fr][j][1][1] = dp[fr][j][0][1];
dp[fr][j][0][1] = inf;
}
}
int ans = inf;
rep (i, 0, bin(n) - 1) {
upmin(ans, min(dp[fr][i][0][0], dp[fr][i][1][1]));
}
printf("%d\n", ans);
}
int main() {
#ifdef LX_JUDGE
freopen("in.txt", "r", stdin);
#endif
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
int kase;
read(kase);
while (kase--) {
Solve();
}
fclose(stdin);
fclose(stdout);
return 0;
}