题解 UVA13154 【Extreme XOR Sum】
无人问津的题目……
题目翻译:
第一行一个数 \(T\),表示数据组数。
对于每一组数据,给出 \(n\) 个数 \(a_0\ldots a_{n-1}\),和 \(m\) 个操作。每个操作包含一个区间 \([l,r]\)。每次取相邻的两个数进行异或,然后将得到的新数列不断进行上述操作,直到只剩下一个数,输出这个数。
注意:原数列不会改变。
题目分析:
我们来简单模拟一下样例中第三个询问。
4,6,7
(4^6),(6^7)
(4^6^6^7)=(4^7)=14
我们可以观察到,在最后的答案中,三个数分别被使用了 \(1\),\(2\),\(1\) 次。
再来模拟一组,当 \(l=0,r=3\) 时。
1,4,6,7
(1^4),(4^6),(6^7)
(1^4^4^6),(4^\6^6^7)
(1^4^4^6^4^6^6^7)=(1^4^6^7)=4
我们观察到,这四个数分别被使用了 \(1\),\(3\),\(3\),\(1\) 次。
有没有感觉很像杨辉三角?事实上就是。观察变化的过程。对于同一个数,记它在第 \(i\) 次操作后在数列中第 \(j\) 个位置出现的次数为 \(f(i,j)\),容易发现:
我们的递推式类似于杨辉三角。可以理解为一个倒着的杨辉三角。当询问的长度为 \(len\) 时,在原数列中第 \(i\) 个数在答案中出现的次数就是 \(\dbinom i {len}\)。
然鹅,我们的询问最长有 \(10^4\),而且没有取模,不可能直接暴力计算。注意到一个数 \(k\) 的异或存在 \(k\operatorname{xor}k=0\),我们只需要知道一个数被使用次数的奇偶性即可(使用偶数次相当于没用,奇数次相当于用了一次),然后将奇数次的对应的 \(a_i\) 异或起来即可。
由于询问很多,我们可以预处理当长度为 \(i\) 的时候,哪些位置上的数用了奇数次。用 vector 存一下就行。
另外,使用递推计算 \(\dbinom j i\) 时应该用滚动数组,否则内存分分钟爆炸。
code:
#include<bits/stdc++.h>
#define reg register
#define F(i,a,b) for(reg int i=(a);i<=(b);++i)
using namespace std;
inline int read();
const int N=1e4+10;
int T,n,Case,m;
int a[N];
bool c[2][N];//滚动
vector<int>f[N];//储存奇数的位置
inline void init() {//预处理
reg int p;
c[1][0]=1;
f[1].push_back(0);
F(i,2,10000) {
p=i&1;
c[p][0]=c[p][i-1]=1;
F(j,1,i-2)c[p][j]=c[p^1][j-1]^c[p^1][j];
F(j,0,i-1)if(c[p][j])f[i].push_back(j);
}
}
int main() {
init();
T=read();
while(T--) {
n=read();
F(i,1,n)a[i]=read();
m=read();
reg int l,r;
printf("Case %d:\n",++Case);
F(i,1,m) {
l=read()+1,r=read()+1;
reg int len=r-l+1,x=0;
for(reg int j=0; j<f[len].size(); ++j) {
x^=a[l+f[len][j]];//将使用次数是奇数的位上的数 xor 起来。
}
printf("%d\n",x);
}
}
return 0;
}
inline int read() {
reg int x=0;
reg char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
欢迎交流讨论,请点个赞哦~
本文来自博客园,作者:Maplisky,转载请注明原文链接:https://www.cnblogs.com/lbh2021/p/14919873.html