P4098 [HEOI2013]ALO

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

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,这样就需要进行特判,细节巨多无比所以不建议用这种做法。

 

P4098 [HEOI2013]ALO
  1 #include<iostream>
  2 #include<string>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<map>
  6 #include<algorithm>
  7 #include<stack>
  8 #include<queue>
  9 #include<vector>
 10 #define maxn 50005
 11 using namespace std;
 12 
 13 inline int read()
 14 {
 15     int x=1,res=0;
 16     char c=getchar();
 17     while(c<'0'||c>'9')
 18     {
 19         if(c=='-')
 20         x=-1;
 21         c=getchar();
 22     }
 23     while(c>='0'&&c<='9')
 24     {
 25         res=res*10+(c-'0');
 26         c=getchar();
 27     }
 28     return res*x;
 29 }
 30 
 31 int n,tot,ans;
 32 int a[maxn],f[maxn][32],lg[maxn],tree[maxn*32][2],last[maxn*32];
 33 int root[maxn];
 34 int l,r;
 35 
 36 int zuo(int l,int r,int rr)
 37 {
 38     if(l>r) return 0; 
 39     int s=lg[r-l+1];
 40     int u=max(f[l][s],f[r-(1<<s)+1][s]);
 41     if(u<=a[rr]) 
 42     {
 43         return 0;
 44     }
 45     else
 46     {
 47         while(l<=r)
 48         {
 49             int mid=(l+r)>>1;
 50             s=lg[r-mid+1];
 51             if(max(f[mid][s],f[r-(1<<s)+1][s])>a[rr])
 52             {
 53                 l=mid+1;
 54             }
 55             else
 56             {
 57                 r=mid-1;
 58             }
 59         }
 60         return r;
 61     }
 62 }
 63 
 64 int you(int l,int r,int ll)
 65 {
 66     if(l>r) return 0;
 67     int s=lg[r-l+1];
 68     int u=max(f[l][s],f[r-(1<<s)+1][s]);
 69     if(u<=a[ll]) 
 70     {
 71         return 0;
 72     }
 73     else
 74     {
 75         while(l<=r)
 76         {
 77             int mid=(l+r)>>1;
 78             s=lg[mid-l+1];
 79             if(max(f[l][s],f[mid-(1<<s)+1][s])>a[ll])
 80             {
 81                 r=mid-1;
 82             }
 83             else
 84             {
 85                 l=mid+1;
 86             }
 87         }
 88         return l;
 89     }
 90 }
 91 
 92 int ask(int l,int val,int k,int now)
 93 {
 94     if(k<0) return val^a[last[now]];
 95     int c=(val>>k)&1;
 96     if(last[tree[now][c^1]]>=l)
 97     {
 98         return ask(l,val,k-1,tree[now][c^1]);
 99     }
100     else
101     {
102         return ask(l,val,k-1,tree[now][c]);
103     }
104 }
105 
106 void trie(int i,int k,int l,int r)
107 {
108     if(k<0)
109     {
110         last[r]=i;
111         return;
112     }
113     int c=(a[i]>>k)&1;
114     if(l) tree[r][c^1]=tree[l][c^1];
115     tree[r][c]=++tot;
116     trie(i,k-1,tree[l][c],tree[r][c]);
117     last[r]=max(last[tree[r][0]],last[tree[r][1]]);
118 }
119 
120 int main()
121 {
122     n=read();lg[0]=-1;
123     last[0]=-10;
124     for(int i=1;i<=n;i++)
125     {
126         a[i]=read();
127         root[i]=++tot;
128         trie(i,30,root[i-1],root[i]);
129     }
130     for(int i=1;i<=n;i++)
131     {
132         f[i][0]=a[i];
133         lg[i]=lg[i>>1]+1;
134     }
135     for(int j=1;j<=20;j++)
136     for(int i=1;i+(1<<j)-1<=n;i++)
137     {
138         f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
139     }
140     for(int i=1;i<=n;i++)
141     {
142         int l1=-1,l2=-1,r1=-1,r2=-1,pd=0;
143         l1=zuo(1,i,i);
144         if(l1!=0)
145         {
146             l2=zuo(1,l1-1,i);
147             if(l2==0)
148             l2=0;
149         }
150         else
151         {
152             l2=0;
153         }
154         r1=you(i,n,i);
155         if(r1!=0)
156         {
157             r2=you(r1+1,n,i);
158             if(r2==0)
159             r2=n+1;
160         }
161         else
162         {
163             pd=1;r1=r2=n+1;
164         }
165         if(l1)
166         {
167             ans=max(ans,ask(l2+1,a[i],30,root[r1-1]));
168         }
169         if(pd==0)
170         {
171             ans=max(ans,ask(l1+1,a[i],30,root[r2-1]));
172         }
173     }
174     cout<<ans;
175     return 0;
176 }
ST表+二分

 

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

P4098 [HEOI2013]ALO
 1 #include<iostream>
 2 #include<string>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstring>
 6 #include<map>
 7 #include<algorithm>
 8 #include<stack>
 9 #include<queue>
10 #include<vector>
11 #define maxn 50005
12 using namespace std;
13 
14 struct edge
15 {
16     int a,b;
17 }g[maxn];
18 
19 inline int read()
20 {
21     int x=1,res=0;
22     char c=getchar();
23     while(c<'0'||c>'9')
24     {
25         if(c=='-')
26         x=-1;
27         c=getchar();
28     }
29     while(c>='0'&&c<='9')
30     {
31         res=res*10+(c-'0');
32         c=getchar();
33     }
34     return res*x;
35 }
36 
37 int n,tot,ans;
38 int a[maxn],last[maxn*32],tree[maxn*32][2],root[maxn],lx[maxn],rx[maxn];
39 
40 void trie(int i,int k,int l,int r)
41 {
42     if(k<0)
43     {
44         last[r]=i;
45         return;
46     }
47     int c=(a[i]>>k)&1;
48     if(l) tree[r][c^1]=tree[l][c^1];
49     tree[r][c]=++tot;
50     trie(i,k-1,tree[l][c],tree[r][c]);
51     last[r]=max(last[tree[r][0]],last[tree[r][1]]);
52 }
53 
54 int ask(int now,int val,int k,int l)
55 {
56     if(k<0) return val^a[last[now]];
57     int c=(val>>k)&1;
58     if(last[tree[now][c^1]]>=l)
59     {
60         return ask(tree[now][c^1],val,k-1,l);
61     }
62     else
63     {
64         return ask(tree[now][c],val,k-1,l);
65     }
66 }
67 
68 bool cmp(edge x,edge y)
69 {
70     return x.a<y.a;
71 }
72 
73 int main()
74 {
75     n=read();
76     last[0]=-10;
77     for(int i=1;i<=n;i++)
78     {
79         a[i]=read();
80         g[i].a=a[i];g[i].b=i;
81         lx[i]=i-1;rx[i]=i+1;
82         root[i]=++tot;
83         trie(i,30,root[i-1],root[i]);
84     }
85     sort(g+1,g+1+n,cmp);
86     for(int i=1;i<=n;i++)
87     {
88         int v=g[i].b;
89         int l=lx[v],r=rx[v];
90         lx[r]=l;rx[l]=r;
91         if(l!=0)
92         ans=max(ans,ask(root[r-1],g[i].a,30,lx[l]+1));
93         if(r!=n+1)
94         ans=max(ans,ask(root[rx[r]-1],g[i].a,30,l+1));
95     }
96     cout<<ans;
97     return 0;
98 }
链表做法

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

上一篇:洛谷 4099 [HEOI2013]SAO——树形DP


下一篇:luogu P4099 [HEOI2013]SAO