南昌邀请赛网络赛K题 MORE XOR 打表,前缀异或和

题目链接

https://nanti.jisuanke.com/t/38230

题意

给你一个整数n,然后给n个整数,编号1到n。

定义f(L,R)函数为区间 L到R的异或和。

定义g(L,R)函数为 所有f(i,j)的异或和,其中L<=i<=j<=R;

定义w(L,R)函数为 所有g(i,j)的异或和,其中L<=i<=j<=R;

q次查询,每次输入区间 i,j,求w(i,j).

样例

输入

1
5
1 2 3 4 5
5
1 3
1 5
1 4
4 5
3 5

输出

2
4
0
1
4

题解

先打个表找规律

打表程序如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=30;
int a[maxn];
int f[maxn][maxn];
int g[maxn][maxn];
int w[maxn][maxn];
int n=30;
void get(int x){
    for(int i=0;i<n;i++){
        if(x>>i&1) cout<<i+1<<" ";
    }
    printf("\n");
}
int main(){
    for(int i=1;i<=n;i++) a[i]=1<<(i-1);
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            for(int k=i;k<=j;k++){
                f[i][j]^=a[k];
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
                int s=0;
            for(int x=i;x<=j;x++){
                for(int y=x;y<=j;y++){
                   s^=f[x][y];
                }
            }
              g[i][j]=s;
        }
    }
     for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
                int s=0;
            for(int x=i;x<=j;x++){
                for(int y=x;y<=j;y++){
                    s^=g[x][y];
                }
            }
              w[i][j]=s;
        }
    }
    for(int i=1;i<=n;i++) printf("%d        ",i),get(w[1][i]);
    return 0;
}

打出来的表是w(1,R)的表。 

解释一下这个表,比如R是25 ,结果就是a[1],a[5],a[9],a[13],a[17],a[21],a[25]这些数据的异或和。

根据L到R的区间长度可以分为4种情况。

区间长度x=R-L+1

如果x%4==0输出0

如果x%4==1 结果是  a[L] ,a[L+4],a[L+8]....a[R]的异或和

如果x%4==3, 先将L加1,R减1,   然后结果是 a[L] ,a[L+4],a[L+8]....a[R]的异或和

如果x%4==2 ,结果是x==1和x==3这两种情况的结果异或一下。

所以我们处理一下前缀,就可以每次查询以O(1)的复杂度计算出来。

AC代码
 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int a[maxn];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",a+i);
        for(int i=5;i<=n;i++) a[i]^=a[i-4];
        int q;
        scanf("%d",&q);
        while(q--){
            int i,j;
            scanf("%d%d",&i,&j);
            int x=j-i+1;
            if(x%4==0){
                printf("0\n");
            }else if(x%4==1){
                int ans;
                if(i>=4) ans=a[j]^a[i-4];
                else ans=a[j];
                printf("%d\n",ans);
            }else if(x%4==3){
                i++;j--;
                int ans;
                if(i>=4) ans=a[j]^a[i-4];
                else ans=a[j];
                printf("%d\n",ans);
            }else{
                 int ans;
                if(i>=4) ans=a[j]^a[i-4];
                else ans=a[j];
                i++,j--;
                if(i>=4) ans=ans^a[j]^a[i-4];
                else ans=ans^a[j];
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值