有一個長度爲 的自然數序列 ,要求將這個序列分成至少 個連續子段
每個子段的價值爲該子段的所有數的按位異或
要使所有子段的價值按位與的結果最大,輸出這個最大值
組詢問
實際上數據範圍可開大很多
我們貪心的一位一位的確定最終答案,即看當前考慮的位能否爲
記表示前個數的異或和,表示異或
設當前考慮到了第位
令
一段區間如果是一個合法的區間,可以得到
於是我們得到了一個的方程
其中
枚舉位是的,這樣就可以此題了
實際這個可以進一步優化
可以推出
即要將到這段作爲一個子段必須滿足上面的條件
因爲題目是至少段,所以分的越多越好
則我們可以考慮完的最優答案後將作爲第一關鍵字存進
這樣一次轉移就是的
複雜度爲
/*******************************
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;
}
如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧