费马大定理以及求解a^2+b^2=c^2的奇偶数列法则

<一>费马大定理:a^n+b^n=c^n 当n大于2时无正整数解.

<二>求解a^2+b^=c^2可以使用a值奇偶法则:1、当a=2*n时,b=n^2-1,c=n^2+1   2、当a=n*2+1时,b=n^2+(n+1)^2-1,c=n^2+(n+1)^2

从a值奇偶法则可以得到:

(1)无论c=n^2+1(还是c=n^2+(n+1)^2),转化成一元二次方程可得:n^2+1-c=0(n^2+(n+1)^2-c=0)的解由韦达定理容易的出n1+n2<=0

也就是说当c确定时,该方程最多只有一组解,但是当a或者b确定时,就有可能有两组解,因为a和b本身就可以互换,得到的满足条件的n可能有两个

(2)只要记住c与另一边的差值为1或者2就可以很容易地求出a,b,c

转载于:https://www.cnblogs.com/MekakuCityActor/p/9828235.html

<think>好的,我现在需要决这个关于圆上2N个点用N条不相交线段连接的方案数的问题。首先,我得理这个问题到底是怎么回事。题目是说,圆上有2N个不同的点,用N条线段把它们两两连接,使得所有的线段都不相交。然后要计算这样的方案数,结果对1e8+7取模。输入是一个正整数N,N可以到2999,所以需要高效的算法。 首先,我得回忆一下有没有类似的问题或者数学模型。好像这是一个经典的组合数学问题,可能涉及到卡特兰数(Catalan numbers)。卡特兰数经常出现在各种组合问题中,比如括号匹配、多边形划分、进出栈序等。比如,n对括号的正确匹配数就是第n个卡特兰数。而这里的问题可能也是类似的。 那卡特兰数是不是适用于这个问题呢?假设是的,那么答案应该是第N个卡特兰数,模1e8+7。比如样例输入是24,输出是4057031。我需要验证一下这个是否正确。比如,当N=1,圆上有两个点,只能连一条线段,方案数是1,对应卡特兰数C1=1。当N=2,圆上四个点,有两种方式连成两段不相交的线段,所以C2=2。这样看来,可能确实是卡特兰数。 但怎么证明呢?这里的问题是否和卡特兰数的递推关系一致?比如,卡特兰数的递推式是C0=1,Cn+1=Σ Ci*C(n-i) ,i从0到n。这个问题的分方式是否与这个递推式相符? 假设圆上的点按顺针顺序编号为0到2N-1。那么,固定一个点,比如0号点,它必须和另一个点连接。假设0号点连接到第k号点,那么这条线段将圆分成两个部分:左边的点和右边的点。左边有k-1个点(假设k是奇数的话),但可能不是这样。或者更准确地说,当连接0和k,线段将圆分割成两个区域,每个区域内部的点数必须是偶数,否则无法用线段完全配对。所以k必须是奇数,因为这样剩下的点数在两个区域都是偶数。例如,假设总共有2N点,当连接0和k,k必须是奇数,这样剩下的两个部分各有偶数个点。例如,当k=1,中间没有点,两边各剩下2N-2点?或者可能不是这样? 或者,可能正确的分法是,当连接0和2m+1,左边的点数是2m,右边的点数是2(N -m -1)。这样,左边的部分可以有m对,右边的部分可以有N -m -1对。因此,总方案数是C0*C(n-1) + C1*C(n-2) + ... + C(n-1)*C0,也就是卡特兰数的递推式。因此,答案确实是第N个卡特兰数,模1e8+7。 那现在问题转化为如何计算第N个卡特兰数模1e8+7的值。这里N可以达到2999,所以需要高效的算法。卡特兰数的计算公式有多种,比如: Cn = (1/(n+1)) * C(2n, n) 或者递推式: C0 = 1 Cn+1 = Σ Ci * Cn-i ,i从0到n,对于n>=0. 或者另一个递推式: Cn = (2*(2n-1)/(n+1)) * Cn-1 这第三个递推式可能更高效,因为可以线性间计算。例如,从C0开始,逐步计算到CN,每次用前一个结果乘以2*(2n-1)/(n+1)。但是这里的除法如何处理模运算? 因为模数是1e8+7(即100000007),它是一个大质数吗?需要确认。假设是的,那么我们可以用模逆元来处理除法。因为当模数p是质数,对于分母的数,可以计算其逆元,然后乘法代替除法。 例如,计算Cn,每次的递推式是: Cn = ( (2*(2n -1) ) * Cn-1 ) / (n+1) 在模运算下,这相当于: Cn = [ (2*(2n -1) mod MOD ) * Cn-1 ] * inv(n+1) mod MOD 其中inv(n+1)是n+1在模MOD下的逆元。 所以,这样的递推式可以在O(N)间内完成。对于n从1到N,每次计算Cn需要一次乘法、一次模逆元的计算。而模逆元可以用快速幂法计算,或者预处理所有逆元。 但n的范围是到2999,所以预处理逆元到3000即可。这样,我们可以预先计算逆元数组inv[1...3000],然后直接使用。 所以步骤大概是这样的: 1. 预计算逆元inv[1...N+1],其中inv[i]是i在模MOD下的逆元。这可以通过费马小定理,因为MOD是质数的话,inv(i) = i^(MOD-2) mod MOD。或者,用递推的方法预处理逆元,但费马小定理对于i不超过3000来说,快速幂足够快。 2. 初始化C[0] = 1. 3. 对于每个i从1到N,计算C[i] = C[i-1] * 2*(2i-1) * inv[i+1] mod MOD. 这样,总的间复杂度是O(N log MOD),或者如果预处理逆元的话,可以O(N)。不过对于N=3e3来说,计算每个逆元的间可能足够。 现在,我们需要确认MOD是否为质数。100000007,这看起来像是一个常见的质数,比如10^8+7。查询后可知,100000007确实是一个质数。所以,可以使用费马小定理计算逆元。 因此,算法的大致步骤是: - 预计算逆元数组inv,其中inv[i] = pow(i, MOD-2, MOD). 然后,利用递推式: C0 = 1 对于n >=0 到 N-1: C_{n+1} = C_n * 2*(2n+1) * inv[n+2] mod MOD 或者,可能我的递推式是否有错误? 原卡特兰数的递推式是: C0 = 1 Cn = sum_{i=0}^{n-1} Ci * C_{n-1 -i} 但另一种递推式是: Cn = (2(2n-1)/(n+1)) * C_{n-1} 比如,C1 = (2*1)/(2) * C0 = 1. C2 = (2*3)/3 * C1= 2*3/3=2 → C2=2. C3 = (2*5)/4 * C2= 10/4 *2 =5 → C3=5,这符合卡特兰数的前几项。 所以这个递推式是正确的。因此,我们可以用这个递推式来计算。 所以,在程序中,我们需要从C0开始,逐步计算到CN。例如,对于n从1到N,计算: C(n) = (2*(2n-1) * C(n-1)) / (n+1) 这里的分母是n+1,所以在模运算中需要用逆元。例如: C(n) = [ (2*(2n-1) mod MOD ) * C(n-1) mod MOD ] * inv(n+1) mod MOD 其中inv(n+1)是n+1的模逆元。 所以,在代码中,我们可以预先计算每个n对应的逆元,或者在每次计算用快速幂求逆元。 例如,对于n从1到N,每次需要计算inv[n+1]。当n可以达到2999,所以n+1可以达到3000。此,对于每个i=1到3000,计算inv[i] = pow(i, MOD-2, MOD),其中MOD=1e8+7。 预处理所有inv数组: 预处理inv数组的大小是3000,每个元素可以通过快速幂计算。 在C++中,可以预先计算一个inv数组,然后使用。 所以,程序的结构大致如下: - 读取N. - 预处理inv数组,inv[i] = pow(i, MOD-2, MOD),其中i从1到N+1. - 初始化C=1(C0=1). - 对于i从1到N: 计算当前项的C_i = C_{i-1} * 2*(2i-1) % MOD. 然后乘以 inv[i+1],再取模MOD. 将结果保存为当前的C. - 最终的C就是结果,输出即可。 例如,当输入N=24,输出应为4057031。这个程序是否能得到正确的结果? 现在,需要测试这个思路是否正确。 比如,当N=1,程序计算C1= (2*1 * C0)/2 = (2*1 *1)/2=1,正确。 当N=2,C2= (2*3 *1)/3=6/3=2,正确。 N=3: (2*5 *2)/4=20/4=5,正确。 所以,递推式是正确的。 接下来,代码部分需要考虑大数的模运算。因为当N是2999,计算过程中可能数值很大,但因为每一步都取模,所以不会有溢出的问题,只要使用足够大的数据类型,比如long long. 现在,编写C++代码的思路: 1. 预计算逆元数组,inv[1...N+1]. 对于每个i,计算inv[i] = pow_mod(i, MOD-2, MOD). 实现pow_mod可以用快速幂函数。 MOD的值是1e8+7,即100000007. 2. 初始化catalan为1(C0). 3. 对于每个n从1到N: catalan = catalan * 2*(2n-1) % MOD; catalan = catalan * inv[n+1] % MOD; 这样循环后,catalan即为CN. 现在的问题是,如何高效计算逆元数组。例如,当N是2999,最大需要计算的inv[i]是3000。 快速幂函数的间复杂度是O(log MOD),对于每个i来说,这需要大约25次迭代(因为MOD是1e8+7,大约是2^27左右)。所以总的间复杂度是O(N log MOD),对于N=3e3来说,是可行的。 因此,代码的大致结构: #include <iostream> using namespace std; const int MOD = 100000007; long long pow_mod(long long a, long long b, int mod) { long long res = 1; while (b > 0) { if (b % 2 == 1) { res = (res * a) % mod; } a = (a * a) % mod; b /= 2; } return res; } int main() { int N; cin >> N; if (N == 0) { cout << 1 << endl; return 0; } // 预处理逆元数组,inv[1..N+1] vector<long long> inv(N + 2); for (int i = 1; i <= N + 1; ++i) { inv[i] = pow_mod(i, MOD - 2, MOD); } long long catalan = 1; // C0 for (int n = 1; n <= N; ++n) { catalan = catalan * 2 * (2 * n - 1) % MOD; catalan = catalan * inv[n + 1] % MOD; } cout << catalan << endl; return 0; } 这样应该可以得到正确的结果。测试样例输入24,输出4057031是否符合? 需要验证。例如,当N=24,运行这段代码,是否得到4057031? 可能需要考虑是否数据类型溢出。例如,当计算2*(2n-1)*catalan,中间结果可能超过long long的范围吗? 例如,当n=29992n-1=5997. 2*5997=11994. catalan的值是模1e8+7的,所以每次catalan在模之后是小于MOD的。所以每次计算catalan = (catalan * 11994) mod MOD。然后乘以inv[3000] mod MOD. 由于MOD是约1e8,每次乘法在long long范围内不会有溢出,因为long long可以存储到约9e18,所以中间结果不会超过这个范围。因此,代码是正确的。 那么,这个代码是否能通过样例? 输入24,输出4057031。测试一下: 假设当N=24,程序按照递推式计算的结果是否等于该值。可能需要手动计算或者信任递推式的正确性。因为根据题目的样例,该输出是正确的,所以代码应该正确。 综上,该问题的决方案是计算第N个卡特兰数模1e8+7的值,通过递推式高效计算。</think>### 方法思路 该问题要求计算圆上2N个点用N条不相交线段连接的方案数,结果对100000007取模。通过分析可知,这等价于计算第N个卡特兰数。卡特兰数的递推公式为: $$ C_n = \frac{2(2n-1)}{n+1} \cdot C_{n-1} $$ 利用递推式结合模运算和逆元,可在O(N)间内高效求解。 ### 决代码 ```cpp #include <iostream> #include <vector> using namespace std; const int MOD = 100000007; // 快速幂计算逆元 long long pow_mod(long long a, long long b, int mod) { long long res = 1; while (b > 0) { if (b % 2 == 1) res = (res * a) % mod; a = (a * a) % mod; b /= 2; } return res; } int main() { int N; cin >> N; vector<long long> inv(N + 2); for (int i = 1; i <= N + 1; ++i) { inv[i] = pow_mod(i, MOD - 2, MOD); } long long catalan = 1; // C0 = 1 for (int n = 1; n <= N; ++n) { catalan = catalan * 2 % MOD; catalan = catalan * (2 * n - 1) % MOD; catalan = catalan * inv[n + 1] % MOD; } cout << catalan << endl; return 0; } ``` ### 代码释 1. **快速幂求逆元**:通过费马小定理,计算每个数在模100000007下的逆元。 2. **预处理逆元数组**:预计算1到N+1的逆元,避免重复计算。 3. **递推计算卡特兰数**:从C0开始,逐步递推计算到CN,每一步均取模确保数值不溢出。 4. **间复杂度**:O(N),适用于N≤2999的规模,高效可靠。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值