Codeforces Round #326 (Div. 1) E. Duff as a Queen

题意:

给出一个长度为n的数组,要求支持一下两种操作:

1.对于l <= i <= r,a[i] ^= k

2.询问[l,r]的分数

区间[l,r]的分数定义为,能使用[l,r]内的数字异或出的数字的个数,一个都不取就是0


solution:

引理1:对于[l,r]中的数字,如果能构造出b1,b2,b3,...,bk,那么,一定能构造出b1 ^ b2 ^ b3 ^ ... ^ bk

把每个bi的构造方案看成是一个n位二进制数码,引理1得证。

构造数组{bn},使得bi = ai ^ ai-1,b1 = a1,那么,有ai = b1 ^ b2 ^ b3 ^ ... ^ bi

引理2:score{an} == score{bn}

因为{bn}中的每个数都可以由{an}构造出来,由引理1,{bn}的任意组合也能由{an}构造。

因此,有score{bn} <= score{an}。同理可得,score{an} <= score{bn}

因此,score{an} == score{bn},引理2得证。

最后,对于原数列{an},构造出{bn},区间修改就能转变为单点修改,询问的话就用线段树维护每段区间的xor线性基,复杂度O(nlog^3n)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 2E5 + 20;
const int T = 4;
const int N = 30;

struct data{
	int a[N]; data(){memset(a,0,sizeof(a));}
	data operator + (const data &b)
	{
		data c;
		for (int i = 0; i < N; i++) c.a[i] = a[i];
		for (int i = 0; i < N; i++)
		{
			if (!b.a[i]) continue; int x = b.a[i];
			for (int j = i; j < N; j++)
				if (x & (1<<j))
				{
					if (c.a[j]) x ^= c.a[j];
					else {c.a[j] = x; break;}
				}
		}
		return c;
	}
}c[maxn*T],ret;

int n,m,mi[N + 1],A[maxn],sum[maxn];

int getint()
{
	char ch = getchar(); int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

void Build(int o,int l,int r)
{
	if (l == r)
	{
		for (int i = l; i <= n; i += i&-i) sum[i] ^= A[l];
		for (int i = 0; i < N; i++)
			if (A[l] & (1<<i)) {c[o].a[i] = A[l]; break;}
		return;
	}
	int mid = (l + r) >> 1;
	Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
	c[o] = c[o<<1] + c[o<<1|1];
}

void Modify(int o,int l,int r,int pos,int t)
{
	if (l == r)
	{
		memset(c[o].a,0,sizeof(c[o].a)); A[l] ^= t;
		for (int i = l; i <= n; i += i&-i) sum[i] ^= t;
		for (int i = 0; i < N; i++)
			if (A[l] & (1<<i)) {c[o].a[i] = A[l]; break;}
		return;
	}
	int mid = (l + r) >> 1;
	if (pos <= mid) Modify(o<<1,l,mid,pos,t);
	else Modify(o<<1|1,mid+1,r,pos,t);
	c[o] = c[o<<1] + c[o<<1|1];
}

void Query(int o,int l,int r,int ql,int qr)
{
	if (ql <= l && r <= qr) {ret = ret + c[o]; return;}
	int mid = (l + r) >> 1;
	if (ql <= mid) Query(o<<1,l,mid,ql,qr);
	if (qr > mid) Query(o<<1|1,mid+1,r,ql,qr);
}

int Sum(int pos)
{
	int ret = 0;
	for (int i = pos; i > 0; i -= i&-i) ret ^= sum[i];
	return ret;
}

int main()
{
	
	n = getint(); m = getint();
	for (int i = 1; i <= n; i++) A[i] = getint();
	for (int i = n; i > 1; i--) A[i] = A[i] ^ A[i - 1];
	Build(1,1,n); mi[0] = 1;
	for (int i = 1; i <= N; i++) mi[i] = mi[i-1] << 1;
	
	while (m--)
	{
		int typ = getint(),l,r;
		l = getint(); r = getint();
		if (typ == 1)
		{
			int k = getint(); Modify(1,1,n,l,k);
			if (r < n) Modify(1,1,n,r + 1,k);
		}
		else
		{
			memset(ret.a,0,sizeof(ret.a));
			int tot = Sum(l),Ans = 0;
			for (int i = 0; i < N; i++)
				if (tot & (1<<i)) {ret.a[i] = tot; break;}
			if (l < r) Query(1,1,n,l + 1,r);
			for (int i = 0; i < N; i++) if (ret.a[i]) ++Ans;
			printf("%d\n",mi[Ans]);
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值