【CF1100F】 Ivan and Burgers (分治+线性基)

description

戳我看题目(づ ̄3 ̄)づ╭❤~

solution

异或和最大 ——关联线性基

线性基:

  1. 原序列的每一个数都能由线性基里若干个数异或得到
  2. 线性基里若干个数的异或结果不可能为0

如果直接线段树合并线性基时间复杂度是无法接受

离线下来,考虑分治

一样的,只在左区间或右区间的分治下去处理,考虑询问跨越了中点 m i d mid mid

处理 [ l , m i d ] [l,mid] [l,mid]区间每个点的后缀线性基, [ m i d + 1 , r ] [mid+1,r] [mid+1,r]区间每个点的前缀线性基

暴力合并线性基即可

code

#include <cstdio>
#include <cstring>
#define maxn 500005
int n, Q;
int a[maxn], ql[maxn], qr[maxn], p[maxn];
int left[maxn], right[maxn], ans[maxn];

struct node {
	int f[20];
	
	void insert( int x ) {
		for( int i = 19;~ i;i -- )
			if( ( 1 << i ) & x ) {
				if( ! f[i] ) { f[i] = x; break; }
				else x ^= f[i];
			}
	}
	
	void clear() {
		memset( f, 0, sizeof( f ) );
	}
	
}base[maxn];

int merge( node x, node y ) { //线性基合并 
	int num = 0;
	for( int i = 19;~ i;i -- )
		x.insert( y.f[i] );
	for( int i = 19;~ i;i -- )
		if( ( num ^ x.f[i] ) > num ) num ^= x.f[i];
	return num;
}

void solve( int L, int R, int l, int r ) {
	if( L > R || l > r ) return;
	if( l == r ) {
		for( int i = L;i <= R;i ++ )
			ans[p[i]] = a[l];
		return;
	}
	int mid = ( l + r ) >> 1, lenl = 0, lenr = 0;
	//暴力重构区间[l',r']的线性基
	base[mid].clear(); //不要忘记清空了!! 
	base[mid].insert( a[mid] );
	for( int i = mid - 1;i >= l;i -- )
		base[i] = base[i + 1], base[i].insert( a[i] );
	for( int i = mid + 1;i <= r;i ++ )
		base[i] = base[i - 1], base[i].insert( a[i] );
	for( int i = L;i <= R;i ++ ) {
		int id = p[i];
		if( ql[id] <= mid ) {
			if( qr[id] <= mid ) //完全在左区间 递归处理 
				left[++ lenl] = id;
			else
				ans[id] = merge( base[ql[id]], base[qr[id]] );
		}
		else //完全在右区间 递归处理 
			right[++ lenr] = id;
	}
	for( int i = 1;i <= lenl;i ++ ) p[L + i - 1] = left[i];
	for( int i = 1;i <= lenr;i ++ ) p[L + lenl + i - 1] = right[i];
	solve( L, L + lenl - 1, l, mid );
	solve( L + lenl, L + lenl + lenr - 1, mid + 1, r );
}

int main() {
	scanf( "%d", &n );	
	for( int i = 1;i <= n;i ++ )
		scanf( "%d", &a[i] );
	scanf( "%d", &Q );
	for( int i = 1;i <= Q;i ++ ) {
		scanf( "%d %d", &ql[i], &qr[i] );
		p[i] = i;
	}
	solve( 1, Q, 1, n );
	for( int i = 1;i <= Q;i ++ )
		printf( "%d\n", ans[i] );
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值