题解 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)\),容易发现:

\[f_{i,j}=f_{i-1,j}+f_{i-1,j+1} \]

我们的递推式类似于杨辉三角。可以理解为一个倒着的杨辉三角。当询问的长度为 \(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;
}

AC

欢迎交流讨论,请点个赞哦~

posted @ 2021-06-22 18:41  Maplisky  阅读(44)  评论(0编辑  收藏  举报