【欧拉降幂】【欧拉定理】子序列权值乘积

159 阅读4分钟

前置知识

欧拉定理

若 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1,则 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)} \equiv 1 \pmod{m} aφ(m)≡1(modm)

扩展欧拉定理

a b ≡ { a b   m o d   φ ( m ) , gcd ⁡ ( a , m ) = 1 , a b , gcd ⁡ ( a , m ) ≠ 1 , b < φ ( m ) , a ( b   m o d   φ ( m ) ) + φ ( m ) , gcd ⁡ ( a , m ) ≠ 1 , b ≥ φ ( m ) . ( m o d m ) a^b \equiv \begin{cases} a^{b \bmod \varphi(m)}, &\gcd(a,m) = 1, \\ a^b, &\gcd(a,m)\ne 1, b < \varphi(m), \\ a^{(b \bmod \varphi(m)) + \varphi(m)}, &\gcd(a,m)\ne 1, b \ge \varphi(m). \end{cases} \pmod m ab≡⎩⎪⎨⎪⎧​abmodφ(m),ab,a(bmodφ(m))+φ(m),​gcd(a,m)=1,gcd(a,m)​=1,b<φ(m),gcd(a,m)​=1,b≥φ(m).​(modm)

费马小定理

p为素数, g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1,则 a p − 1 ≡ 1 ( m o d p ) a^{p - 1} \equiv 1 \pmod{p} ap−1≡1(modp)

注:若m为素数,则 φ ( m ) = m − 1 \varphi(m) = m-1 φ(m)=m−1


题目

题目链接:
ac.nowcoder.com/acm/contest…

给一个数组,求数组所有非空子序列的权值之积。
数组的权值:数组中的最大值乘最小值


长度为n的数组,有 2 n − 1 2^n-1 2n−1个非空子序列,枚举必然超时。

将数组排序,当排序了以后,如果确定了最大值和最小值,它们中间的数取不取对结果是没有影响的。

因此,通过这种方式可以枚举最大值和最小值,中间的部分规约在一起,利用欧拉降幂进行计算,这样复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn),这样还是不够。

如果区间长度 ( r − l + 1 ) (r-l+1) (r−l+1)为 k k k ,那么中间的数有【】或【不取】两种状态,因此有 2 k − 2 2^{k-2} 2k−2的权值贡献。

对于长度固定的情况,那么贡献的次数固定。这部分可以利用乘法分配律合并起来。

这样最终只需要枚举区间长度就可以。

举例

以长度为5的数组举例 [ 1 , 2 , 3 , 4 , 5 ] [1,2,3,4,5] [1,2,3,4,5],假设已经排好序

  • 枚举长度 2,最终带来的权值收益是

( 1 ∗ 2 ) 2 0 ∗ ( 2 ∗ 3 ) 2 0 ∗ ( 3 ∗ 4 ) 2 0 ∗ ( 4 ∗ 5 ) 2 0 (1*2)^{2^0}*(2*3)^{2^0}*(3*4)^{2^0}*(4*5)^{2^0} (1∗2)20∗(2∗3)20∗(3∗4)20∗(4∗5)20

  • 枚举长度 3,最终带来的权值收益是

( 1 ∗ 3 ) 2 1 ∗ ( 2 ∗ 4 ) 2 1 ∗ ( 3 ∗ 5 ) 2 1 (1*3)^{2^1}*(2*4)^{2^1}*(3*5)^{2^1} (1∗3)21∗(2∗4)21∗(3∗5)21

  • 枚举长度 4,最终带来的权值收益是

( 1 ∗ 4 ) 2 2 ∗ ( 2 ∗ 5 ) 2 2 (1*4)^{2^2}*(2*5)^{2^2} (1∗4)22∗(2∗5)22

  • 枚举长度 5,最终带来的权值收益是

( 1 ∗ 5 ) 2 3 (1*5)^{2^3} (1∗5)23

根据公式 a t ∗ b t = ( a ∗ b ) t a^t*b^t=(a*b)^t at∗bt=(a∗b)t ,这些幂相同的底数都是可以合并的。

合并之后:

l e n = 2 len=2 len=2时, ( 1 ∗ 2 2 ∗ 3 2 ∗ 4 2 ∗ 5 ) 2 0 (1*2^2*3^2*4^2*5)^{2^0} (1∗22∗32∗42∗5)20
l e n = 3 len=3 len=3时, ( 1 ∗ 2 ∗ 3 2 ∗ 4 ∗ 5 ) 2 1 (1*2*3^2*4*5)^{2^1} (1∗2∗32∗4∗5)21
l e n = 4 len=4 len=4时, ( 1 ∗ 2 ∗ 4 ∗ 5 ) 2 2 (1*2*4*5)^{2^2} (1∗2∗4∗5)22
l e n = 5 len=5 len=5时, ( 1 ∗ 5 ) 2 3 (1*5)^{2^3} (1∗5)23
可以发现规律,每次都少了两边的一个值,可以先计算出数组的平方之积,每个长度可以用lr表示两边的元素,将这个两个元素除去(要用逆元),最后乘上贡献即可。

欧拉降幂:

根据费马小定理,当 a a a 和 p p p 互素时,有 a p − 1 % p = 1 a^{p-1}\%p=1 ap−1%p=1

那么我们要计算 a b % p a^b\%p ab%p 的值,可以先把 b b b 写成 b = ( p − 1 ) u + v b=(p-1)u+v b=(p−1)u+v 的形式
即求 a ( p − 1 ) u + v = a ( p − 1 ) u ∗ a v a^{(p-1)u+v}=a^{(p-1)u}*a^v a(p−1)u+v=a(p−1)u∗av,前部分模 p p p为1,所以答案就是 a v a^v av,而 v = b % ( p − 1 ) v=b\%(p-1) v=b%(p−1)

也就是说,我们要求 a b % p a^b\%p ab%p,如果b 是个比较大的数(可能是个幂的形式),可以先让 b(p-1) 取模,是不影响最终答案的正确性的(前提是 ap 互素)。这个取模就叫做欧拉降幂。

因为len=1没有计算,所以先将答案乘上len=1时产生的贡献

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5; 
typedef long long ll;
const ll mod = 1e9+7;

ll ksm(ll a,ll b,ll p)
{
	ll res = 1;
	while(b)
	{
		if(b&1) res = res * a % p ;
		b >>= 1;
		a = a * a % p; 
	}
	return res;
}
ll inv(ll x)
{
	return ksm(x,mod-2,mod);
}
int main()
{
	int n;
	cin>>n;
	vector<ll>a(n);
	ll res = 1, tmp = 1;
	for(int i=0;i<n;i++) 
	{
		cin>>a[i];
		tmp = tmp * a[i] % mod * a[i] % mod;
	}
	//len=1
	res = res * tmp % mod;
	sort(a.begin(),a.end());
	int l = 0, r = n-1;
	while(l < n-1)
	{
		tmp = tmp * inv(a[l++]) % mod * inv(a[r--]) % mod;
		res = res * ksm(tmp,ksm(2,l-1,mod-1),mod) % mod;
	}
	
	cout<<res<<endl;
	return 0;
}