收录两题 还有大家千万别用uva的题目来源 这oj真的垃圾 毁我青春
题意:给一个长度为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;
}
题意:给一个长度为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开始)
#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;
}