题解 P7335【[JRKSJ R1] 异或】
题意
给你 和序列 ,选出 个不交区间 ,求出
。
保证数据随机。
思路
:重构全文。
暴力寻找左右端点即可。
我们首先思考怎么处理 的情况,这是一个经典问题。
设 ,则 ,我们考虑对于每个 快速找出 使得上式最大,对 建出 ,在上面贪心地选择不同的儿子即可。
我们设 ,即在 中选 个数使它们异或和最大。
设处理到第 个数,第 个区间的答案为 ,则 。
暴力处理,时间复杂度 。
发现上面的状态转移方很难优化,因为 是变化的,如果 不变的话,.
于是我们打一个表观察一下发现 固定时,对于 , 不同的值很少,于是我们可以想到把相同的 压缩起来,对于 相同的一段区间 ,我们求出 即可。
我们发现 数组单调不递减, 数组单调不递增,于是上面的式子就等价于 。
现在我们来证明这个算法的复杂度正确。
由于数据随机生成,当我们插入一个数时,设此时已经插入 个数,这时候一共有 个异或和,这个数可以贡献 个异或和。即有 的几率成为 ,这个数贡献了 的期望。
所以 中的数的个数的期望为 ,所以期望时间复杂度为 。
期望得分 分。
另外,由于数据随机,暴力转移较少的数的 也可以过,对策在考虑。
具体实现见以下代码(我很久以前写的,有点丑请见谅):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int maxn;
struct trie{//01-Trie
int cnt;
int son[90005][2];
trie(){
cnt=1;
}
void clear(){
for(int i=1;i<=cnt;i++){
son[i][0]=son[i][1]=0;
}
cnt=1;
}
void insert(int x){
int rt=1;
for(int i=29;i>=0;i--) {
if(!son[rt][(x>>i)&1])
son[rt][(x>>i)&1]=++cnt;
rt=son[rt][(x>>i)&1];
}
}
int find(int x){
int rt=1,ans=0;
for(int i=29;i>=0;i--){
if(son[rt][!((x>>i)&1)]) rt=son[rt][!((x>>i)&1)],ans+=1<<i;
else rt=son[rt][(x>>i)&1];
}
return ans;
}
}t;
struct line{
int l,r;
int val;
line(){
l=r=val=0;
}
line(int a,int b,int c){
l=a,r=b,val=c;
}
};
struct array{
line s[100];
int len;
array(){
len=0;
}
int operator[](const int &x){
return s[x].val;
}
inline void init(int i){
len=1;
s[1].l=0;
s[1].r=i;
s[1].val=0;
}
inline void insert(int x,int v){//压缩ans数组
if(v==s[len].val){
s[len].r=x;
}else{
len++;
s[len].l=x;
s[len].r=x;
s[len].val=v;
}
}
inline int top(){
return s[len].val;
}
}ans[3005];
int a[3005];
int n,k;
void init(){//预处理ans
for(int i=0;i<=n;i++){
t.clear();
t.insert(a[i]);
ans[i].init(i);
for(int j=i+1;j<=n;j++){
int now=t.find(a[j]);
ans[i].insert(j,max(ans[i].top(),t.find(a[j])));
t.insert(a[j]);
}
}
}
namespace IO{
//read()
}using namespace IO;
ll dp[3005][2];
int main(){
n=read(),k=read();
for(int i=1;i<=n;i++)
a[i]=read()^a[i-1];
init();
for(int i=k-1;i<=n;i++){
dp[i][k&1]=ans[i].top();
}
for(int d=k-1;d;d--){//dp
int s=d&1;//滚动数组优化
for(int l=n;l>=0;l--){
ll mx=0;
int r=1;
mx=ans[l][r]+dp[l][!s];
for(r++;r<=ans[l].len;r++){
if(dp[ans[l].s[r].l][!s]+ans[l].top()<mx) break;//剪枝
mx=max(mx,dp[ans[l].s[r].l][!s]+ans[l][r]);
}
dp[l][s]=mx;
}
}
cout<<dp[0][1];
}
再见 qwq~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】