牛客练习赛81 B.小 Q 与彼岸花(Tire树上贪心或区间DP)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛81 B.小 Q 与彼岸花(Tire树上贪心或区间DP)相关的知识,希望对你有一定的参考价值。

LINK

考虑对每个 [ 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][j1])

然后再和 a i ⊕ a j a_i\\oplus a_j aiaj取个最大值就好了

#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)的主要内容,如果未能解决你的问题,请参考以下文章

牛客练习赛81 D.小 Q 与树(树剖+套路)

牛客网Nowcoder 牛客练习赛13 A.幸运数字Ⅰ B.幸运数字Ⅱ(数组或者dfs) C.幸运数字Ⅲ(思维)

牛客练习赛51 F.ABCBA(树上主席树维护子序列)

NowCoder牛客练习赛7-A.骰子的游戏 B.购物-优先队列

牛客练习赛56 E.小雀和他的王国 tarjan+生成树上求直径

Educational Codeforces Round 81 (Rated for Div. 2) B. Infinite Prefixes