D
e
s
c
r
i
p
t
i
o
n
\mathcal{Description}
Description
有一个长度为
n
n
n的自然数序列
a
a
a,要求将这个序列分成至少
m
m
m 个连续子段
每个子段的价值为该子段的所有数的按位异或
要使所有子段的价值按位与的结果最大,输出这个最大值
T
T
T组询问
T
≤
10
,
n
,
m
≤
1000
,
a
i
≤
2
30
T\leq 10,n,m\leq 1000,a_i\leq 2^{30}
T≤10,n,m≤1000,ai≤230
S
o
l
u
t
i
o
n
\mathcal{Solution}
Solution
实际上数据范围可开大很多
我们贪心的一位一位的确定最终答案,即看当前考虑的位能否为
1
1
1
记
s
i
s_i
si表示前
i
i
i个数的异或和,
⨁
\bigoplus
⨁表示异或
设当前考虑到了第
b
b
b位
令
r
e
s
=
a
n
s
∣
(
1
<
<
b
)
res=ans|(1<<b)
res=ans∣(1<<b)
一段区间
[
j
+
1
,
i
]
[j+1,i]
[j+1,i]如果是一个合法的区间,可以得到
(
s
i
⨁
s
j
)
&
r
e
s
=
r
e
s
\left(s_i\bigoplus s_j\right)\&res=res
(si⨁sj)&res=res
于是我们得到了一个
n
2
l
o
g
n^2log
n2log的
D
P
DP
DP方程
f
i
=
m
a
x
f
i
,
f
j
+
1
f_i=max{f_i,f_j+1}
fi=maxfi,fj+1其中
(
s
i
⨁
s
j
)
=
r
e
s
\left(s_i\bigoplus s_j\right)=res
(si⨁sj)=res
枚举位是
l
o
g
log
log的,这样就可以
A
C
AC
AC此题了
实际这个
D
P
DP
DP可以进一步优化
(
s
i
⨁
s
j
)
&
r
e
s
=
r
e
s
\left(s_i\bigoplus s_j\right)\&res=res
(si⨁sj)&res=res可以推出
(
s
i
&
r
e
s
)
⨁
(
s
j
&
r
e
s
)
=
r
e
s
\left(s_i \& res\right)\bigoplus \left(s_j\& res\right)=res
(si&res)⨁(sj&res)=res
⇒
s
i
&
r
e
s
=
(
s
j
&
r
e
s
)
⨁
r
e
s
\Rightarrow s_i \& res=\left(s_j\& res\right)\bigoplus res
⇒si&res=(sj&res)⨁res
即要将
s
i
s_i
si到
s
j
s_j
sj这段作为一个子段必须满足上面的条件
因为题目是至少
m
m
m段,所以分的越多越好
则我们可以考虑完
s
i
s_i
si的最优答案后将
s
i
⨁
r
e
s
s_i\bigoplus res
si⨁res作为第一关键字存进
s
e
t
set
set
f
i
=
f
i
n
d
(
s
i
⨁
r
e
s
)
f_i=find(s_i\bigoplus res)
fi=find(si⨁res)
这样一次转移就是
l
o
g
log
log的
复杂度为
n
l
o
g
2
nlog^2
nlog2
C o d e \mathcal{Code} Code
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月26日 星期六 09时18分19秒
*******************************/
#include <cstdio>
#include <fstream>
#include <cstring>
#include <set>
#define mp make_pair
using namespace std;
const int maxn = 2003;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int n,m,T,ans;
int a[maxn],s[maxn];
set < pair<int,int> > v;
set < pair<int,int> > :: iterator it,nx;
//{{{solve
void solve (int x)
{
int res=ans|(1<<x);
bool flag;
v.clear();
for (int i=1;i<=n;++i){
int val=s[i]&res;
v.insert(mp(val,0));
nx=it=v.lower_bound(mp(val,0));
++nx;
while (nx!=v.end()&&nx->first==val){
v.erase(it);
it=nx,++nx;
}
if (it->second==0){
if (val==res){
v.insert(mp(val^res,1));
if (i==n) flag=it->second+1>=m;
}
}
else{
v.insert(mp(val^res,(it->second)+1));
if (i==n) flag=it->second+1>=m;
}
}
if (flag) ans=res;
}
//}}}
int main()
{
cin>>T;
while (T--){
cin>>n>>m;
ans=0;
for (int i=1;i<=n;++i){
cin>>a[i];
s[i]=s[i-1]^a[i];
}
for (int i=29;~i;--i) solve(i);
printf("%d\n",ans);
}
return 0;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧