牛客练习赛81 B.小 Q 与彼岸花(Tire树上贪心或区间DP)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛81 B.小 Q 与彼岸花(Tire树上贪心或区间DP)相关的知识,希望对你有一定的参考价值。
考虑对每个 [ 1 , i ] [1,i] [1,i]建立一颗 01 T i r e \\rm 01Tire 01Tire树
那么我们就可以利用前缀和 O ( 1 ) O(1) O(1)得到区间 [ l , r ] [l,r] [l,r]的 T i r e \\rm Tire Tire树
在树上从高位往低位 d f s dfs dfs爆搜两个数每一位选 0 0 0还是选 1 1 1
设两个数分别走到树上的节点 l e \\rm le le和 r e \\rm re re
①.若 l e le le能走到 0 0 0且 r e re re能走到 1 1 1就去搜
②.若 l e le le能走到 1 1 1且 r e re re能走到 0 0 0就去搜
③.若 l e , r e le,re le,re能同时走到 0 0 0就去搜
④.若 l e , r e le,re le,re能同时走到 1 1 1就去搜
乍一看每一层有四种选择,总体复杂度似乎是 4 11 4^{11} 411
然而如果执行过①②,说明①②产生的答案一定比③④优秀
因为包含高位一,所以不可能同时执行,单词复杂度降到 2 11 2^{11} 211
注意
T i r e \\rm Tire Tire树空间要开到 2 12 2^{12} 212
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5009;
int n,m,a[maxn],ans;
int zi[maxn][1<<12][2],id,val[maxn][1<<12],ID[maxn];
void insert(int id,int x)
{
int now = 0;//当前的序号
for(int i=10;i>=0;i--)
{
int ok = ((x>>i)&1);
if( !zi[id][now][ok] ) zi[id][now][ok] = ++ID[id];
now = zi[id][now][ok]; val[id][now]++;
}
}
//le表示第一个数dfs到树上的点,re表示第二个数dfs到树上的点
void dfs(int r,int le,int re,int deep,int res)
{
if( deep==-1 ){ ans = max( ans,res ); return; }
int le0 = val[r][ zi[r][le][0] ], le1 = val[r][ zi[r][le][1] ];
int re0 = val[r][ zi[r][re][0] ], re1 = val[r][ zi[r][re][1] ];
int flag = 0;
if( le0 && re1 )
dfs( r,zi[r][le][0],zi[r][re][1],deep-1,res|(1<<deep) ),flag = 1;
if( le1 && re0 )
dfs( r,zi[r][le][1],zi[r][re][0],deep-1,res|(1<<deep) ),flag = 1;
if( flag ) return;
if( le0 && re0 )
{
if( le!=re || le0>=2 )
dfs( r,zi[r][le][0],zi[r][re][0],deep-1,res );
}
if( le1 && re1 )
{
if( le!=re || le1>=2 )
dfs( r,zi[r][le][1],zi[r][re][1],deep-1,res );
}
}
void solve(int l,int r)
{
for(int i=1;i<=ID[r];i++) val[r][i] -= val[l-1][i];
ans = 0;
dfs(r,0,0,10,0);
printf("%d\\n",ans );
for(int i=1;i<=ID[r];i++) val[r][i] += val[l-1][i];
}
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++) scanf("%d",&a[i] );
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
insert( i,a[j] );
while( m-- )
{
int l,r; scanf("%d%d",&l,&r);
if( l==r ) cout << 0 << endl;
else solve( l,r );
}
}
然后我发现我是个脑瘫,这就是个区间 d p dp dp
f [ i ] [ j ] = m a x ( f [ i + 1 ] [ j ] , f [ i ] [ j − 1 ] ) \\rm f[i][j]=max(f[i+1][j],f[i][j-1]) f[i][j]=max(f[i+1][j],f[i][j−1])
然后再和 a i ⊕ a j a_i\\oplus a_j ai⊕aj取个最大值就好了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+10;
int n,m,a[maxn],f[maxn][maxn];
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++) scanf("%d",&a[i] );
for(int i=1;i<n;i++) f[i][i+1] = a[i]^a[i+1];
for(int l=3;l<=n;l++)
for(int i=1;i+l-1<=n;i++)
{
int j = i+l-1;
f[i][j] = max( f[i+1][j],f[i][j-1] );
f[i][j] = max( f[i][j],a[i]^a[j] );
}
while( m-- )
{
int l,r; scanf("%d%d",&l,&r);
printf("%d\\n",f[l][r] );
}
}
以上是关于牛客练习赛81 B.小 Q 与彼岸花(Tire树上贪心或区间DP)的主要内容,如果未能解决你的问题,请参考以下文章
牛客网Nowcoder 牛客练习赛13 A.幸运数字Ⅰ B.幸运数字Ⅱ(数组或者dfs) C.幸运数字Ⅲ(思维)
NowCoder牛客练习赛7-A.骰子的游戏 B.购物-优先队列
牛客练习赛56 E.小雀和他的王国 tarjan+生成树上求直径
Educational Codeforces Round 81 (Rated for Div. 2) B. Infinite Prefixes