Description
给定一个长度为 n 的数列 𝑎,请回答 𝑞 次询问,每次给定 𝑙,𝑟请求出 的值,其中 𝑝=1,145,141。
Input
第一行是两个整数,依次表示数列长度 𝑛 和询问次数 q。
第二行有 n 个整数,第 𝑖个整数表示 ai。
接下来 𝑞行,每行两个整数 𝑙,𝑟表示一次询问。
Output
为了避免大量数据输出导致超时,请输出一行一个整数表示所有询问的答案的按位异或和。
Sample 1
Inputcopy | Outputcopy |
---|---|
5 3 1 2 3 4 5 2 3 3 4 2 4 | 18 |
这道题感觉你弄会两个点就可以写出来,第一个点就是求逆元(用快速幂,或者说费马小定理,因为费马小定理的模板基本上就是快速幂),第二个点是用前缀积(因为题目中是一段区间相乘的结果),对了还需要注意题目最后说的按位异或和,如果我前面说的两个知识点你都会,那么恭喜你,这道题你绝对会写,不会的话建议你把这两个知识点学一学。废话不多说,上代码
这里我说一下为什么要用逆元来算,就比如一个a数组,a[1]=1,a[2]=2,a[3]=3,那么前缀积sum数组,sum[1]=1,sum[2]=2,sum[3]=6,求二到三之间的数,你直接用(sum[3]/sum[1])%p就行了,结果为6,等价于a[2]*a[3]=6,而这里(sum[3]/sum[1])%p就可以运用到逆元,直接sum[3]*(sum[1]^p-2)%p,将除法转化成乘法,再用快速幂写,很快
我逆元只懂得这些皮毛,你们可以看看其他大佬的关于逆元的文章
#include"iostream"
#include<cstdio>
using namespace std;
const int N=1e6+10;
int p=1145141;
long long a[N],sum[N];
long long qm(long long base,long long power)//这个是快速幂的模版!!!,不会有太大的改变
{
long long result=1;
while(power!=0)
{
if(power%2!=0)
result=base*result%p;
power/=2;
base=base*base%p;
}
return result;
}
int main()
{
int n,q;
scanf("%d %d",&n,&q);
sum[0]=1;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=(sum[i-1]*a[i])%p;//数组sum是数组a的前缀积,因为数可能会很大,所以要模p
}
int s=0;
while(q--)
{
int x,y;
scanf("%d %d",&x,&y);
s=s^sum[y]*qm(sum[x-1],p-2)%p;逆元(用快速幂实现的)
}
printf("%d",s);
return 0;
}