最近这个家伙去哪了,为啥一直不更博客了呢?原来他被老师逼迫去补了一周的文化课,以至于不会把班里的平均分拉掉太多。好了,我们来看下面这道题目:

P4098 [HEOI2013]ALO

题目描述

Welcome to ALO ( Arithmetic and Logistic Online)。这是一个 VR MMORPG, 如名字所见,到处充满了数学的谜题

现在你拥有 n 颗宝石,每颗宝石有一个能量密度,记为 ai,这些宝石的能量 密度两两不同。现在你可以选取连续的一些宝石(必须多于一个)进行融合,设 为 ai, ai+1, …, aj,则融合而成的宝石的能量密度为这些宝石中能量密度的次大值 与其他任意一颗宝石的能量密度按位异或的值,即,设该段宝石能量密度次大值 为 k,则生成的宝石的能量密度为 max{k xor ap | ap ≠ k , i ≤ p ≤ j}

现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最 大

输入输出格式

输入格式:

第一行,一个整数 n,表示宝石个数

第二行,n 个整数,分别表示 a1 至 an,表示每颗宝石的能量密度,保证对于 i ≠ j 有 ai ≠ aj

输出格式:

输出一行一个整数,表示最大能生成的宝石能量密度

输入输出样例

输入样例#1:

5
9 2 1 4 7
输出样例#1:

14

首先关于异或和什么的问题,一看就要用到trie,然而又是有区间限制,所以一定要访问某个历史版本,自然就是可持久化trie了。那么最重要的问题来了,我们如何找到以每个数为次大值最大的区间呢?首先我们可以想到,如果一个区间里一个数为次大值,那么最大值要么在这个数的左边,要么在这个数的右边,所以我么就可以维护每个数左边比它大的两个数l1、l2,和右边比这个数大的两个数r1、r2这样区间就是(l1,r2)和(l2,r1)这两个区间,那么如何求解每个数的l1,l2,r1,r2呢,这里有两种方法。

第一种,我们可以用ST表预处理然后左右进行二分查找,但是考虑到有些数可能没有l1,l2,r1,r2,这样就需要进行特判,细节巨多无比所以不建议用这种做法。

 #include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#define maxn 50005
using namespace std; inline int read()
{
int x=,res=;
char c=getchar();
while(c<''||c>'')
{
if(c=='-')
x=-;
c=getchar();
}
while(c>=''&&c<='')
{
res=res*+(c-'');
c=getchar();
}
return res*x;
} int n,tot,ans;
int a[maxn],f[maxn][],lg[maxn],tree[maxn*][],last[maxn*];
int root[maxn];
int l,r; int zuo(int l,int r,int rr)
{
if(l>r) return ;
int s=lg[r-l+];
int u=max(f[l][s],f[r-(<<s)+][s]);
if(u<=a[rr])
{
return ;
}
else
{
while(l<=r)
{
int mid=(l+r)>>;
s=lg[r-mid+];
if(max(f[mid][s],f[r-(<<s)+][s])>a[rr])
{
l=mid+;
}
else
{
r=mid-;
}
}
return r;
}
} int you(int l,int r,int ll)
{
if(l>r) return ;
int s=lg[r-l+];
int u=max(f[l][s],f[r-(<<s)+][s]);
if(u<=a[ll])
{
return ;
}
else
{
while(l<=r)
{
int mid=(l+r)>>;
s=lg[mid-l+];
if(max(f[l][s],f[mid-(<<s)+][s])>a[ll])
{
r=mid-;
}
else
{
l=mid+;
}
}
return l;
}
} int ask(int l,int val,int k,int now)
{
if(k<) return val^a[last[now]];
int c=(val>>k)&;
if(last[tree[now][c^]]>=l)
{
return ask(l,val,k-,tree[now][c^]);
}
else
{
return ask(l,val,k-,tree[now][c]);
}
} void trie(int i,int k,int l,int r)
{
if(k<)
{
last[r]=i;
return;
}
int c=(a[i]>>k)&;
if(l) tree[r][c^]=tree[l][c^];
tree[r][c]=++tot;
trie(i,k-,tree[l][c],tree[r][c]);
last[r]=max(last[tree[r][]],last[tree[r][]]);
} int main()
{
n=read();lg[]=-;
last[]=-;
for(int i=;i<=n;i++)
{
a[i]=read();
root[i]=++tot;
trie(i,,root[i-],root[i]);
}
for(int i=;i<=n;i++)
{
f[i][]=a[i];
lg[i]=lg[i>>]+;
}
for(int j=;j<=;j++)
for(int i=;i+(<<j)-<=n;i++)
{
f[i][j]=max(f[i][j-],f[i+(<<(j-))][j-]);
}
for(int i=;i<=n;i++)
{
int l1=-,l2=-,r1=-,r2=-,pd=;
l1=zuo(,i,i);
if(l1!=)
{
l2=zuo(,l1-,i);
if(l2==)
l2=;
}
else
{
l2=;
}
r1=you(i,n,i);
if(r1!=)
{
r2=you(r1+,n,i);
if(r2==)
r2=n+;
}
else
{
pd=;r1=r2=n+;
}
if(l1)
{
ans=max(ans,ask(l2+,a[i],,root[r1-]));
}
if(pd==)
{
ans=max(ans,ask(l1+,a[i],,root[r2-]));
}
}
cout<<ans;
return ;
}

ST表+二分

第二种,我们可以先用双向链表储存每一个数的左右两边的数,然后对序列进行排序,再从最小的元素开始依次查找,每查完一个元素我们就把它从链表里删除,这样链表中存的就一定是每个元素左右两边的第一个比它大的元素那么左边的左边就l2,右边的右边就是r2,这样就实现了查询,另外我们只需特判表头和表尾两个元素就行,实现起来也非常简单。

 #include<iostream>
#include<string>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#define maxn 50005
using namespace std; struct edge
{
int a,b;
}g[maxn]; inline int read()
{
int x=,res=;
char c=getchar();
while(c<''||c>'')
{
if(c=='-')
x=-;
c=getchar();
}
while(c>=''&&c<='')
{
res=res*+(c-'');
c=getchar();
}
return res*x;
} int n,tot,ans;
int a[maxn],last[maxn*],tree[maxn*][],root[maxn],lx[maxn],rx[maxn]; void trie(int i,int k,int l,int r)
{
if(k<)
{
last[r]=i;
return;
}
int c=(a[i]>>k)&;
if(l) tree[r][c^]=tree[l][c^];
tree[r][c]=++tot;
trie(i,k-,tree[l][c],tree[r][c]);
last[r]=max(last[tree[r][]],last[tree[r][]]);
} int ask(int now,int val,int k,int l)
{
if(k<) return val^a[last[now]];
int c=(val>>k)&;
if(last[tree[now][c^]]>=l)
{
return ask(tree[now][c^],val,k-,l);
}
else
{
return ask(tree[now][c],val,k-,l);
}
} bool cmp(edge x,edge y)
{
return x.a<y.a;
} int main()
{
n=read();
last[]=-;
for(int i=;i<=n;i++)
{
a[i]=read();
g[i].a=a[i];g[i].b=i;
lx[i]=i-;rx[i]=i+;
root[i]=++tot;
trie(i,,root[i-],root[i]);
}
sort(g+,g++n,cmp);
for(int i=;i<=n;i++)
{
int v=g[i].b;
int l=lx[v],r=rx[v];
lx[r]=l;rx[l]=r;
if(l!=)
ans=max(ans,ask(root[r-],g[i].a,,lx[l]+));
if(r!=n+)
ans=max(ans,ask(root[rx[r]-],g[i].a,,l+));
}
cout<<ans;
return ;
}

链表做法

其实还有一个问题,就是在做01trie树的时候,要注意什么时候应该建一棵全0树而什么时候不需要,还有就是last[0]为什么要清为负数,我想这些看似不起眼的问题还是要弄明白比较好,毕竟细节决定成败。

P4098 [HEOI2013]ALO的更多相关文章

  1. P4098 [HEOI2013]ALO 可持久化01Trie

    $ \color{#0066ff}{ 题目描述 }$ Welcome to ALO ( Arithmetic and Logistic Online).这是一个 VR MMORPG, 如名字所见,到处 ...

  2. [LUOGU]P4098[HEOI2013]ALO

    BZOJ上的权限题,流下了贫穷的泪水... 可持久化trie的题. 一开始zz了,看错了题,以为是要把所有的宝石缩起来,后来仔细一看好像只缩一次...昨天刷了一晚上的语文病句题白做了... 这样的话就 ...

  3. BZOJ3166: [Heoi2013]Alo

    3166: [Heoi2013]Alo Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 394  Solved: 204[Submit][Status] ...

  4. bzoj 3166 [Heoi2013]Alo 可持久化Trie

    3166: [Heoi2013]Alo Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1227  Solved: 569[Submit][Status ...

  5. BZOJ 3166: [Heoi2013]Alo

    3166: [Heoi2013]Alo Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 923  Solved: 437[Submit][Status] ...

  6. [BZOJ3166][Heoi2013]Alo 可持久化Trie树

    3166: [Heoi2013]Alo Time Limit: 20 Sec Memory Limit: 256 MB DescriptionWelcome to ALO ( Arithmetic a ...

  7. 【BZOJ3166】[Heoi2013]Alo 可持久化Trie树+set

    [BZOJ3166][Heoi2013]Alo Description Welcome to ALO ( Arithmetic and Logistic Online).这是一个VR MMORPG , ...

  8. Bzoj 3166 [Heoi2013] Alo 题解

    3166: [Heoi2013]Alo Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1118  Solved: 518[Submit][Status ...

  9. 暴力 【p4098】[HEOI2013]ALO

    Description Welcome to ALO ( Arithmetic and Logistic Online).这是一个 VR MMORPG, 如名字所见,到处充满了数学的谜题 现在你拥有 ...

随机推荐

  1. PL2303HX在Windows 10下面不装安装驱动的解决办法(Code:10)

    Prolific在很早之前推出了一款名为PL2303HX的芯片, 用于USB转RS232, 这款芯片使用的范围非常广, 并且年代久远. 但是这款芯片因为用的特别多, 所以中国就有很多厂家生产了仿造的P ...

  2. cocos creator入门

    前面的话 Cocos Creator 是一个完整的游戏开发解决方案,包括了 cocos2d-x 引擎的 JavaScript 实现,以及快速开发游戏所需要的各种图形界面工具.Cocos Creator ...

  3. iOS NSInteger 的输出 %d %ld %zd %ld (long)

    NSInteger 输出类型 %zd

  4. 【ML】从特征分解,奇异值分解到主成分分析

    1.理解特征值,特征向量 一个对角阵\(A\),用它做变换时,自然坐标系的坐标轴不会发生旋转变化,而只会发生伸缩,且伸缩的比例就是\(A\)中对角线对应的数值大小. 对于普通矩阵\(A\)来说,是不是 ...

  5. nginx 提示the "ssl" directive is deprecated, use the "listen ... ssl" directive instead

    该问题是由于新版nginx采用新的方式进行监听https请求了 解决方式 在listen中改为 listen 443 ssl; 删除ssl配置 # ssl on; 完美解决: 解决完成前后的配置如下 ...

  6. IDEA升级版本后界面出现变小,字体变细的问题解决

    笔者在今天升级了最新版本的IDEA 2019后发现,该版本的IDEA出现了非常诡异的事情如下图: 升级版本后字体居然发生了变化(通过官方导入的我自己的settings文件)还是出现了问题. 问题解决方 ...

  7. C# http 性能优化500毫秒到 60 毫秒

    偶然发现 C# 的 HttpRequest 要比 Chrome 请求同一Url 慢好多.C# HttpRequest 要500毫秒 而Chrome 只需要 39ms. 作为有责任感的 码农.这个 必须 ...

  8. iOS程序员 如何做到升职加薪,5年 开发经验 码农 笔记送给你!

    前言: 对于我们绝大多数人来说,工作最基本的意义是我们可以通过自己的劳动换取酬劳来养活自己,尤其是对于刚毕业的学生,拥有一份全日制的工作意味着自己走出校园,踏上社会,自己真正开始独立起来. 在养活自己 ...

  9. 四、Tensorflow的分布式训练

    TensorFlow中的集群(cluster)指的是一系列能够针对图(Graph)进行分布式计算任务(task).每个任务是同服务(server)相关联的.TensorFlow中的服务会包含一个用于创 ...

  10. 并发编程之CountDownLatch

    在前面的两篇文章中我们分别用volatile.notify()和wait()分别实现了一个场景,我们再来回顾一下前面的场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一 ...