计蒜客 ACM-ICPC 2017 Asia Xi

收录两题  还有大家千万别用uva的题目来源 这oj真的垃圾 毁我青春

A1607   XOR  

题意:给一个长度为n的序列 给一个数k  每次询问区间L,R  在L,R这些数中选一个子集  使得子集中的元素的异或和与k进行或运算以后取得最大值

显然 由或运算的性质我们知道 k的二进制中为1的位会保留 为了取得最大值 我们得尽量让为0的位变成1      我们可以让序列中每个数都进行 ai&=(~k) 这样的一个运算 那么ai留下来的就是k中没有的位了   然后我们用线段树 维护a数组的区间线性基  对于询问 L,R 我们得到该区间的线性基并且给出最大值mx  最后 k|mx 就是答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+7;
int a[N]; 
int ans[30]; 
int tree[N<<2][30];
void add(int b[],int x) {
	for(int i=27;i>=0;i--) {
		if(x&(1LL<<i)) {
			if(!b[i]) {
				b[i]=x;
				break;
			}
			else x^=b[i];
		}
	}
}
void pushup(int rt) {
	for(int i=27;i>=0;i--)
		tree[rt][i]=tree[rt<<1][i];
	for(int i=27;i>=0;i--) 
		add(tree[rt],tree[rt<<1|1][i]); 
}
void build(int rt,int l,int r) {
	if(l==r) {
		add(tree[rt],a[l]);
		return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	pushup(rt);
}
void query(int rt,int l,int r,int L,int R) {
	if(R<l||L>r) return;
	if(L<=l&&r<=R) {
		for(int i=27;i>=0;i--)
			add(ans,tree[rt][i]);
		return; 
	}
	int mid=(l+r)>>1;
	query(rt<<1,l,mid,L,R);
	query(rt<<1|1,mid+1,r,L,R);
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		int n,q,k;
		scanf("%d%d%d",&n,&q,&k);
		for(int i=1;i<=n;i++) {
			scanf("%d",&a[i]);
			a[i]=a[i]&(~k);
		}
		build(1,1,n);
		while(q--) {
			int l,r;
			scanf("%d%d",&l,&r);
			for(int i=27;i>=0;i--) ans[i]=0;//清空答案  
			query(1,1,n,l,r);
			int res=0;
			for(int i=27;i>=0;i--) { //获得最大值 
				if((res^ans[i])>res) {
					res^=ans[i]; 
				}
			}
			printf("%d\n",res|k);
		}
	}
	return 0;
}

A1613   Sum of xor sum

题意:给一个长度为n的序列  给出区间L,R  求该区间所有子区间的异或和的和

这算是个比较套路的题吧 bzoj上似乎也有类似的题   首先进行二进制拆分  对于每一位 我们做一个前缀异或  然后统计这个区间的前缀异或中0和1的个数就行  因为是前缀异或 我们需要加一个虚点0进去 即a[0]=0;

用sum0[i][j] 表示前i+1个数(包括a[0])在第j位的前缀异或为0的个数 sum1[i][j] 同理 

根据前缀异或的性质  

我们考虑区间L到R第t位的贡献(从0开始)

 ans=(sum[R][t]-sum[L-2][t])*(1<<t)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
int a[N];
const ll mod = 1e9+7;
ll sum1[N][30],sum0[N][30];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		memset(sum0,0,sizeof(sum0));
		memset(sum1,0,sizeof(sum1));
		int n,q;
		scanf("%d%d",&n,&q);
		for(int i = 1; i <= n; i++){
			scanf("%d",&a[i]);
			a[i]^=a[i-1];
		}
		for(int j = 29; j >= 0; j--){
			for(int i = 0; i <= n; i++){
				if(i){
					sum1[i][j]+=sum1[i-1][j];
					sum0[i][j]+=sum0[i-1][j];
				}
				if((a[i]>>j)&1) sum1[i][j]++;
				else sum0[i][j]++;
			}
		}
		for(int i = 1; i <= q; i++){
			int l,r;
			scanf("%d%d",&l,&r);
			ll ans = 0;
			for(int j = 29; j >= 0; j--){
				if(l==1) 
				ans+=((sum1[r][j])*(sum0[r][j])%mod*(1ll<<j))%mod;
				else 
				ans+=((sum1[r][j]-sum1[l-2][j])*(sum0[r][j]-sum0[l-2][j])%mod*(1ll<<j))%mod;
				//printf("sum1[r][j]=%lld sum1[l-1][j]=%lld sum0[r][j]=%lld sum0[l-1][j]=%lld\n",sum1[r][j],sum1[l-1][j],sum0[r][j],sum0[l-1][j]);
				ans%=mod;	
			}	
			printf("%lld\n",ans);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/106909099