收錄兩題 還有大家千萬別用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;
}