F - 分拆素数和

F - 分拆素数和

HPU专题训练(-1)GCD&&素筛&&快速幂_____F - 分拆素数和
把一个偶数拆成两个不同素数的和,有几种拆法呢?
Input输入包含一些正的偶数,其值不会超过10000,个数不会超过500,若遇0,则结束。Output对应每个偶数,输出其拆成不同素数的个数,每个结果占一行。Sample Input
30
26
0
Sample Output
3
2
/*
	暴力 
*/

#include<stdio.h> 
int p(int a)
{
	int z=1;
	if(a<=1)	return 0;
	for(int i=2;i*i<=a;i++)
	{
		if(a%i==0)
		{
			z=0;
			break;
		 } 
	}
	return z;
}
int main()
{
	int x;
	while(scanf("%d",&x)!=EOF)
	{
		if(x==0)	break;
		int sum=0;
		for(int i=2;i<x/2;i++)
		{
			if(p(i)&&p(x-i))	sum++;
		}
		printf("%d\n",sum);
	}
	return 0;
}


<think>嗯,我现在得解决这个问题。题目是让我们计算函数f(n,r)的值,然后对mod取模。那f(n,r)的定义是组合数C(n,r)乘以p(n−r),而p(x)是x的阶乘。所以其实这个f(n,r)等于C(n,r)乘以(n−r)!。那这样的话,把这两个式子代入进去,可能可以简化这个表达式。 首先,组合数C(n,r)的公式是n!/(r!*(n−r)!))。然后乘以(n−r)!的话,那分母里的(n−r)!就会被抵消掉。所以f(n,r)其实就是n!/(r!)。对吧?这样结果就简化为n!/(r!)。这样计算起来是不是会更简单呢? 那这样的话,每个查询的答案就是n!除以r!,然后对mod取模。那问题就转化为如何快速计算n!r!的模逆元,或者如何处理这个除法在模运算中的情况。 不过这里有个问题:当mod不是质数的时候,可能无法直接求逆元。因为如果mod是质数的话,可以用费马小定理求逆元。但如果mod是合数,或者包含多个质因数的话,这时候求逆元可能比较复杂。这时候可能需要将问题转换为乘法,或者采用其他方法。 那原题中的模数是给定的,所以如何处理这个n!/(r!) mod mod的情况是关键。 首先,假设mod是质数的话,那么我们可以预处理阶乘数组fact,以及阶乘的逆元数组inv_fact。这样,对于每个查询n,r,计算fact[n] * inv_fact[r] mod mod即可。但问题是当mod不一定是质数的时候,这种方法可能不可行。 题目中的mod范围是1到1e14,而且对于不同数据点的模有不同的情况。比如,在40%的数据中,mod是质数,而100%的数据中mod可能是任意的数,包括合数或者质数。 那这个时候,如何处理n! / r! mod mod呢?例如,假设mod是一个合数,那么直接计算除法可能有问题,因为r!mod可能不互质,导致逆元不存在。这时候,可能需要将n! / r!分解为连续整数的乘积。因为n! / r!等于(r+1)*(r+2)*...*n。所以,如果我们能预处理每个数的前缀积的话,就可以直接得到这个连续乘积。 那这样的话,我们可以预先计算一个数组prod,其中prod[x]表示1到x的阶乘。然后,对于每个查询nr,当n >= r时,结果为prod[n] / prod[r]。但这样在模运算中如何处理呢? 或者,我们可以将n! / r!看作是(r+1)*(r+2)*...*n,也就是从r+1乘到n的积。这样,如果我们能预处理前缀积数组的话,那么对于每个查询nr,可以快速得到这个区间的乘积。例如,可以预先计算一个数组pre,其中pre[i]是1到i的积,然后pre[n]/pre[r]就是(r+1)*...*n。但是同样,在模运算中除法的问题依然存在。 但这里,当mod不是质数的时候,如何处理这个除法?或者说,是否可以将除法转换为乘法逆元? 假设mod是任意数,且我们需要计算A/B mod mod。那么,只有当Bmod互质的时候,才能找到B的逆元,进而计算A * B^{-1} mod mod。否则,可能无法直接计算逆元。这时候,可能需要将问题拆分成模互质的部分,或者采用其他数学方法。 回到我们的问题,n! / r! = (r+1)*(r+2)*...*n。而我们需要计算这个积对mod取模的结果。这时候,是否可以将这个积直接计算出来,而避免除法? 是的。因为如果我们可以预计算一个数组,保存每个位置的前缀积,然后对于查询nr,我们可以直接计算从r+1到n的乘积。这样,就能避免除法,直接计算乘积模mod的结果。这似乎是一个可行的方案。 那这样的话,我们需要预处理一个数组mul,其中mul[i]表示从1乘到i的结果,即i!。然后,对于每个查询nr,我们要求的是mul[n] / mul[r]。但这样还是涉及到除法的问题。但如果我们预先计算每个数的阶乘模mod的话,那么如何得到这个连续的乘积? 或者,我们可以预计算前缀积数组prefix,其中prefix[i] = (1 * 2 * 3 * ... * i) mod mod。这样,当我们需要计算r+1到n的积时,可以用prefix[n] / prefix[r]。但同样,除法在模运算中的处理还是一个问题。 或者,或许更简单的方法是,直接预计算每个数的阶乘,然后在处理每个查询的时候,计算阶乘[n]乘以逆阶乘[r] mod mod。但逆阶乘的存在与否取决于r!mod是否互质。如果mod是质数,并且r!与mod互质,那么可以计算逆元。但mod可能不是质数,或者r!mod不互质,这时候无法计算逆元。 那这个时候,我们可能需要换一种思路。比如,将n! / r!转化为(r+1)*(r+2)*...*n,然后逐个相乘取模。这样,每个查询的时间复杂度是O(n - r)的时间,这在n到1e5的情况下,q也是1e5的话,这样的复杂度是O(1e10),显然无法通过。 这时候,必须找到一种O(1)或O(log n)处理每个查询的方法。所以,问题转化为如何预处理这些区间的积。 这个时候,我们可以想到分段处理或者前缀积数组,但如何处理模运算中的除法? 或者,我们可以预处理每个数的阶乘,并对每个阶乘保存模mod后的值。这样,当计算n! / r!时,实际上是计算阶乘[n] * inv_fact[r] mod mod。但这里的inv_fact[r]是否存在的条件是fact[r]mod互质。如果mod中存在与fact[r]不互质的因数,那么逆元可能不存在。这种情况下,这种方法是不可行的。 那这个时候,问题变得复杂了。如何处理这种情况? 可能需要将mod分解质因数,然后对于每个阶乘中的质因数进行处理,最后合并结果。或者,对于每个查询的nr,我们计算(r+1)*...*n mod mod的值,而无需除法。 例如,如果预处理一个数组prod,其中prod[i]表示前i个数的乘积模mod。那么,对于区间[r+1, n]的乘积,可以表示为prod[n] * inv(prod[r]) mod mod。但同样,这需要prod[r]mod互质才能求逆。 否则,当prod[r]mod不互质时,这种方法行不通。这个时候,可能无法预处理,只能在每个查询中直接计算这个区间的积。 但这样的话,每个查询的时间复杂度是O(n - r),这在最坏情况下(比如r=0,n=1e5,每个查询都要计算1e5次乘法)无法通过,时间会超限。 所以,这似乎是一个死胡同。那这个时候,我们需要重新审视问题。 原题中,f(n,r) = C(n,r) * p(n−r)。而p(n−r)是(n−r)!。所以原式等于C(n,r) * (n−r)!。而C(n,r)的公式是n!/(r! (n−r)!))。因此,整个式子可以简化为n!/(r! (n−r)! )) * (n−r)! ) =n!/(r! )。所以,这似乎是对的。所以,f(n,r)等于n! / r!。对吧? 那这样的话,问题转化为求n! / r! mod mod的值。这等于(r+1)(r+2)...n mod mod。那如何计算这个积? 现在问题转化为,如何快速计算从r+1到n的积模mod的值。这时候,如果我们能预处理前缀积的话,就可以用前缀积[n] / 前缀积[r] mod mod。但除法在模运算中如何处理? 或者,预处理每个数的前缀积模mod,这样,从r+1到n的乘积等于前缀积[n] * inv(前缀积[r]) mod mod。但inv是否存在取决于前缀积[r]mod是否互质。如果互质的话,那么可以用扩展欧几里得算法求逆。否则,无法求逆元。 那这个时候,如何处理? 这可能取决于mod的性质。例如,如果mod是质数,并且r+1到n中的数可能mod互质,那么可能可行。但对于一般的mod,这可能无法处理。因此,这可能无法统一处理。 这时候,有没有其他方法? 比如,当我们需要计算(r+1)*(r+2)*...*n mod mod时,可以预计算一个二维数组,保存每个可能的rn的积模mod。但这样的话,空间复杂度是O(n^2),对于n到1e5的情况,这显然不现实。 另一个思路是,对于每个查询,直接循环计算从r+1到n的积,并模mod。这样,每个查询的时间复杂度是O(n - r),但在最坏情况下,比如n=1e5,r=0,每个查询需要计算1e5次操作,而q=1e5的话,总时间会是1e10,这显然会超时。 所以,这种方法在时间上是不可行的。必须找到一种更高效的方法。 那这个时候,我们需要回到问题的简化。刚才的推导是否正确? 原问题中的f(n, r) = C(n, r) * p(n−r)。其中C(n,r)是组合数,等于n!/(r! (n−r)! )。而p(n−r)是(n−r)!。所以,相乘之后,得到n!/(r!)。所以,确实是对的。所以,问题转化为计算n!/(r!) mod mod。 那这个式子等于从r+1到n的连续乘积。所以,如何快速计算这个连续乘积模mod的值? 这个时候,如果我们预处理每个数的阶乘模mod,那么n! mod mod等于fact[n],r! mod mod等于fact[r]。那么,n! / r! mod mod等于 (fact[n] / fact[r]) mod mod。但这里的除法在模运算中如何计算? 这等于fact[n] * inv(fact[r]) mod mod,其中inv是模mod的乘法逆元。但只有当fact[r]mod互质时,这个逆元存在。否则,这种方法不适用。 那这个时候,如何处理? 比如,当mod是质数时,我们可以用费马小定理计算逆元。但题目中的mod可能不是质数,或者fact[r]与mod不互质。这时候,无法求出逆元,所以这种方法无法使用。 那这个时候,问题变得困难了。可能只能预处理每个数的阶乘模mod,但如何处理除法? 或者,是否可以分情况讨论: 1. 当mod是质数时,可以用逆元处理。 2. 当mod不是质数时,如何处理? 或者,我们是否可以找到另一种方式,无论mod是否为质数,都能处理? 例如,将问题转换为计算(r+1)*(r+2)*...*n mod mod,这可以看作是一个区间乘积的问题。那么,如果我们能预处理这些区间的乘积,就可以快速回答查询。 那如何预处理呢?比如,可以使用前缀积数组,然后区间乘积等于前缀积[n] / prefix[r]。但同样,模运算中的除法问题。 或者,可以预处理每个位置i的前缀积数组prefix[i],其中prefix[i] = 1*2*3*...*i mod mod。然后,区间[r+1..n]的乘积等于prefix[n] / prefix[r]。但同样,在模运算中,这需要计算prefix[n] * inv(prefix[r]) mod mod。这只能在prefix[r]mod互质时成立。 否则,当prefix[r]mod不互质时,无法求得逆元,这样处理方式就会失败。 所以,这个时候,如果mod是一个任意的数,如何处理? 这时候,可能需要将mod分解质因数,然后处理每个质因数的幂次。但这样可能比较复杂,特别是当mod很大(如1e14)时,分解质因数的时间可能会很高。 这可能不是一个可行的方案。 那这时候,是否还有其他思路? 比如,注意到当mod中存在某个质因数p,并且在r!中p的幂次大于等于mod中的p的幂次,那么,此时r!mod的最大公约数d>1。此时,当计算n! / r!时,可能无法直接取模,因为可能无法整除。或者,是否总能整除? 因为n! / r!等于(r+1)*...*n,所以不管mod是什么,这个乘积的结果总是一个整数。所以,在模mod的时候,这个乘积的结果应该是存在的。也就是说,不管mod如何,只要计算该乘积模mod的值即可。 那这个时候,如果我们能直接计算这个乘积模mod的值,就能得到正确的结果。例如,对于每个查询中的nr,我们计算(r+1)* (r+2) * ... *n mod mod。这并不需要任何除法,而是直接相乘取模。这似乎是一个可行的方法。但问题在于,当nr的范围是1e5,每个查询可能需要O(n - r)次操作,这在最坏情况下,时间复杂度是无法接受的。 比如,假设有1e5次查询,每次查询需要1e5次乘法操作,那么总时间是1e10次操作,这显然会超时。 那这个时候,必须找到一种预处理的方法,使得每个查询可以在O(1)或O(log n)的时间内得到结果。 那么,如何预处理这些区间的乘积呢? 这时,可以考虑分块预处理,或者使用线段树、稀疏表(ST表)等数据结构。例如,使用ST表来预处理区间乘积,这样每个查询可以在O(1)或O(log n)时间内得到结果。 ST表是用于处理静态区间查询的数据结构,适用于那些可以快速合并的运算,比如最小值、最大值、区间GCD等。但乘法运算是否可以用ST表处理呢? 是的。因为ST表的预处理是基于倍增的思想,每个区间的长度是2的幂次,所以可以预处理每个起点开始的不同长度的区间的乘积。这样,对于任意区间[r+1, n],我们可以将其拆分为若干个预先处理好的区间的乘积,然后相乘得到结果。 例如,ST表的每个位置st[k][i]表示从i开始的2^k个数的乘积。然后,对于给定的区间[l, r],我们可以将其分解为若干个这样的块,并相乘这些块的乘积,得到整个区间的乘积。 这样,预处理的时间复杂度是O(n log n),而每次查询的时间复杂度是O(log n)。对于n=1e5来说,预处理时间是O(1e5 * 20) = 2e6,这是可以接受的。而每个查询需要O(20)次操作,1e5次查询总共有2e6次操作,这应该是可以接受的。 那这样的话,问题就转化为如何用ST表预处理区间乘积模mod的值,并在查询时快速得到结果。 那么,具体的实现步骤可能如下: 1. 预处理ST表,其中st[k][i]表示从i开始的连续2^k个数的乘积模mod。比如,k=0时,st[0][i] = (i+1) mod mod(因为i的取值范围是0到n-1,对应的数是1到n?或者可能需要调整索引)。 或者,假设原数组是a[i] = i+1,其中i从0到max_n-1。例如,当i=0时,对应数值1,i=1对应数值2,等等。这样,st表处理的是数组a[0..max_n-1],即数值1,2,3,...,max_n。 那ST表的预处理步骤: 对于每个k从0到log2(max_n),对于每个i,st[k][i] = product of a[i..i+2^k -1] mod mod。 当k=0时,st[0][i] = a[i] mod mod. 当k>0时,st[k][i] = st[k-1][i] * st[k-1][i + 2^{k-1}]} mod mod. 这样,预处理完成后,对于区间[l, r],我们需要找到最大的k,使得2^k <= length of the interval. 然后将区间拆分为若干个块,每个块的长度为2^k,并将这些块的乘积相乘,直到覆盖整个区间。 例如,假设区间长度是 len = r - l + 1,那么我们需要分解成若干个2的幂次长度的块,直到它们的总等于len。这可以通过位操作来实现。 然后,每次查询的时候,将各个块的乘积相乘,得到整个区间的乘积模mod的结果。 这样,每个查询的时间复杂度是O(log n),而预处理的时间复杂度是O(n log n),这在n=1e5的情况下是可行的。 所以,这样处理的话,对于每个查询中的nr,我们需要计算区间[r+1, n]的乘积mod mod的值。也就是,如果n >= r+1的话,否则结果为1?或者当r >=n时,根据题目描述,输入满足1<=r<=n,所以当n=r时,结果是n! / r! = 1. 那如何将r+1到n的区间转换为ST表中的索引? 比如,假设数值是从1到max_n,那么数组a的索引是0到max_n-1。数值x对应的数组a的索引是x-1。例如,数值1对应索引0,数值2对应索引1,依此类推。 因此,当我们要计算从x=r+1到y=n的乘积时,对应的数组区间是a[x-1]到a[y-1],即索引范围是 (r+1-1) 到 (n-1),即 r到n-1的闭区间。因此,区间长度是 n-1 - r +1 = n - r. 所以,我们需要计算数组a的[r, n-1]区间的乘积mod mod的值。这可以通过ST表来实现。 因此,预处理ST表,然后对于每个查询中的nr,如果n < r+1的话,说明区间长度为0,乘积为1?例如,当r =n时,r+1 =n+1> n,所以乘积为1。此时,原式等于n! /n! =1。所以,这种情况需要处理。 在题目描述中,输入的条件是1<=r <=n。因此,当r等于n时,此时f(n,r) = C(n,n) * (n-n)! =1*0! =1*1=1。因此,此时结果应为1。而对应的计算式是n! /n! =1,符合条件。 因此,在代码中,对于每个查询的nr,当r >=n时,结果应为1。但根据题目输入条件,r<=n,所以当r=n时,处理方式正确。 因此,在代码中,当r ==n时,输出1 mod mod即可。 所以,综上所述,解决步骤为: 预处理一个数组的ST表,其中每个元素对应数值i的索引是i-1。例如,数值1的索引是0,数值2的索引是1,等等。这样,当需要计算从r+1到n的乘积时,对应的区间是索引r到n-1。区间的长度是n-1 -r +1 =n -r. 然后,使用ST表查询该区间的乘积mod mod的值。 那现在的问题是,如何实现这个ST表? C++中的实现可能如下: 首先,确定max_n的可能最大值。根据题目,n可以达到1e5,所以max_n是1e5。预处理数组a的长度是1e5,每个元素a[i] =i+1. 预处理log_table数组,其中log_table[i]表示i的最高位2的幂次。例如,log_table[1] =0,log_table[2] =1,log_table[3] =1,log_table[4] =2,等等。这在预处理时可以方便地计算。 然后,预处理st表,其中st[k][i]表示从i开始,长度为2^k的区间的乘积模mod。对于每个k,从0到log_max_level,其中log_max_level是最大的k使得 2^k <=max_n. 预处理之后,每个查询的处理: 输入nr,如果r >=n,则结果为1。否则,计算区间[r+1, n]的乘积mod mod的值。 对应的数组区间是索引r到n-1(因为数值r+1对应的索引是r,数值n对应的索引是n-1)。 所以,区间长度是 (n-1 -r +1) =n -r. 然后,使用ST表查询该区间的乘积。 这样,每个查询的结果就是该区间的乘积mod mod的值。 所以,问题的关键在于如何构建ST表,并实现高效的区间查询。 现在,考虑如何处理mod很大的情况,比如mod到1e14。相乘的时候可能溢出,所以需要用到64位整数类型。例如,在C++中使用unsigned long long或者long long来存储中间结果,并在每次乘法后取模。 那ST表的预处理步骤中的每一步相乘都需要取模,否则数值可能会溢出。 例如,在预处理st[k][i]时,计算st[k-1][i] * st[k-1][i + 2^{k-1}]} mod mod. 这样,可以保证数值不会超过mod的值,从而避免溢出。因为mod的值最大是1e14,而两个这样的数相乘的结果可能达到1e28,超过了64位无符号整型的范围(约1.8e19)。所以,这时候需要使用模运算的特性,或者使用快速乘法的技巧? 或者,在C++中,使用__int128类型来临时保存乘积,然后再取模? 例如,对于ab两个数,计算(a * b) mod mod的时候,可以用__int128来保存乘积,然后取模。例如: uint64_t a, b, mod; __int128 product = (__int128)a * b; uint64_t result = product % mod; 这样,可以避免溢出。因为1e14的平方是1e28,而__int128可以保存到约1e36的数,所以足够。 所以在代码中,所有乘法操作都必须用这样的方式处理,以防止溢出。 因此,在预处理ST表查询时,每一步的乘法都需要使用这样的方式处理,以避免溢出。 综上,代码的大体步骤是: 1. 输入Qmod。 2. 预处理数组a,其中a[i] =i+1,i从0到max_n-1。这里max_n是最大的可能n值,比如1e5. 3. 预处理ST表,其中每个st[k][i]存储从i开始,长度为2^k的区间的乘积mod mod的值。 4. 对于每个查询nr: a. 如果r >=n,输出1 % mod. b. 否则,计算区间[r+1, n]的乘积mod mod,即查询数组a的区间[r到n-1]的乘积mod mod. 5. 输出结果。 现在,如何处理ST表的构建? 例如,预处理log_table数组,或者使用预计算的log2值。或者,在查询时动态计算区间的长度对应的最高幂次。 例如,构建st表的过程: - 首先,计算max_n为1e5,因为题目中的n最多是1e5. - 初始化st数组,其中st的层数为log2(max_n) +1,例如20层足够(因为2^20=1e6,超过1e5). - 预处理st[0][i] =a[i] % mod. - 对于k从1到max_level: for (int i=0; i + (1<<k) <= max_n; i++) { st[k][i] = ( (__int128)st[k-1][i] * st[k-1][i + (1 << (k-1))] ) % mod; } 然后,在查询区间[l, r](闭区间)时,初始结果为1. 变量current_pos = l. 然后,从最大的k开始往下循环,直到处理完整个区间: 例如: while current_pos <= r: 找出最大的s,使得从current_pos开始的2^s长度不超过剩余区间的长度。 s = floor(log2(r - current_pos +1)) res = (res * st[s][current_pos]) % mod. current_pos += (1 << s) 这样,最终得到的res就是区间乘积mod mod的值。 所以,这个查询过程的复杂度是O(log n) per query. 这样,总的时间复杂度是预处理O(n log n) + 查询O(Q log n). 对于n=1e5Q=1e5,这应该是可以接受的。 现在,如何处理输入中的n可能不同的情况?比如,每个查询的n可能不同,并且可能超过预处理时的max_n? 不,题目中的输入描述给出每个查询的nr满足1<=r <=n <=1e5。所以,预处理时可以将max_n设为1e5,这样所有查询的n都不超过1e5。 所以,预处理数组a的大小是1e5,每个元素对应数值i+1. 这样,在预处理的时候,数组a的长度是1e5,每个元素i对应数值i+1. 现在,编写代码的大体结构: 首先,读取Qmod. 然后,预处理数组a,长度max_n=1e5. 预处理st表: max_level = log2(1e5) ≈ 17. 然后,预处理st[0][i] =a[i]%mod. 然后,逐层构建st表. 然后,对于每个查询n, r: if r >=n: 输出1 % mod. else: l =r R =n-1 目标区间是数组中的l到R的闭区间. 计算这个区间的乘积mod mod. 输出这个结果. 现在,如何处理区间查询? 例如,假设l R是数组中的索引(0-based),区间长度是 len = R - l +1. 如果 len ==0,返回1. 否则,按照ST表的查询方式,逐步分解区间。 例如,函数query(l, R): res =1 current = l while current <= R: s = log_table[R - current +1] res = (res * st[s][current]) % mod current += (1 << s) return res 其中,log_table数组预先计算每个长度对应的最大的s,使得 2^s <= len. 比如,log_table数组可以预处理为,log_table[i] = floor(log2(i)). 这可以在预处理阶段完成。例如,log_table的大小是1e5+1,每个元素log_table[i]的值是最大的k,使得 2^k <=i. 这样,在查询时,对于当前的剩余长度len = R - current +1,s =log_table[len]. 这样,预处理log_table数组的代码可以是: log_table[0] =0; // 或者不处理,因为查询时长度至少1? for (int i=2; i<=max_n; i++){ log_table[i] = log_table[i/2] +1; } 或者,可以用更精确的方法计算。 或者,在每次查询时,使用内置函数来计算log2的值,这可能效率不高,但可能可以接受。 或者,使用位运算来找最高位的位置。 例如,可以使用__builtin_clz函数,或者类似的方法,来计算s的值。 例如,对于给定的len = R - current +1: s = 31 - __builtin_clz(len); 在C++中,__builtin_clz返回前导零的个数,所以对于len>=1,可以得到最大的s,使得2^s <= len. 例如,当len=5时,二进制是101,前导零数目是30-2=28?假设是32位整数的话。所以,31-28=3?那2^3=8>5,这可能不对。哦,可能需要使用63 - __builtin_clzll(len) for 64-bit numbers. 这可能更复杂。所以,预处理log_table数组可能更高效。 综上,代码的大体结构如下: 预处理log_table数组,其中log_table[i]是最大的k满足2^k <=i. 预处理st表。 处理每个查询: 输入nr. 如果 r >=n: 输出1 mod mod. 否则: l_index =r r_index =n-1 如果 l_index >r_index: 输出1 mod mod. 否则,调用query函数计算区间[l_index, r_index]的乘积mod mod,并输出. 那现在,如何预处理log_table数组? 例如,max_n是1e5. log_table数组的大小是1e5+2. log_table[0] =0; // 无意义,但不会访问到. log_table[1] =0. log_table[2] =1. log_table[3] =1. log_table[4]=2,依此类推. 预处理方式可以是: log_table[1] =0; for(int i=2; i<=max_n; i++){ log_table[i] = log_table[i>>1] +1; } 这应该可以正确预处理每个i对应的最大的k,使得2^k <=i. 例如,i=5: i>>1=2 → log_table[2] =1 → log_table[5] =1+1=2? 但是2^2=4 <=5,所以是正确的. 是的。所以,log_table[i]的值是最大的k使得2^k <=i. 这样,预处理后的log_table数组可以正确工作. 综上,现在编写代码的各个部分。 现在,测试示例: 例如,示例输入: 3 1007 5 3 →n=5, r=3 →r+1=4到5的乘积是4*5=20 mod 1007 →20. 第二个查询:n=2, r=1 →r+1=2到2的乘积是2 mod 1007 →2. 第三个查询:n=8, r=4 →r+1=5到8的乘积是5*6*7*8= 1680. 1680 mod 1007=1680-1007=673. 与示例输出一致。所以,这种方法是正确的。 综上,这个问题的正确解法是预处理ST表来处理区间乘积查询,从而快速计算每个查询中的(r+1)*...*n mod mod的值。这样,无论mod是否是质数,都可以正确处理,无需逆元。</think>```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int MAX_N = 1e5; const int LOG = 17; // 2^17=131072 > 1e5 ull st[LOG + 1][MAX_N]; int log_table[MAX_N + 1]; void preprocess(int max_n, ull mod) { for (int i = 0; i < max_n; ++i) { st[0][i] = (i + 1) % mod; } for (int k = 1; k <= LOG; ++k) { for (int i = 0; i + (1 << k) <= max_n; ++i) { int j = i + (1 << (k - 1)); st[k][i] = ( (__int128)st[k - 1][i] * st[k - 1][j] ) % mod; } } log_table[1] = 0; for (int i = 2; i <= MAX_N; ++i) { log_table[i] = log_table[i >> 1] + 1; } } ull query(int l, int r, ull mod) { ull res = 1; while (l <= r) { int k = log_table[r - l + 1]; res = ( (__int128)res * st[k][l] ) % mod; l += (1 << k); } return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); int Q; ull mod; cin >> Q >> mod; int max_n = MAX_N; preprocess(max_n, mod); while (Q--) { int n, r; cin >> n >> r; if (r >= n) { cout << 1 % mod << '\n'; continue; } int l_index = r; int r_index = n - 1; ull ans = query(l_index, r_index, mod); cout << ans << '\n'; } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值