题目大意
给定n个数,求其中连续子序列的gcd(最大公因数)的积mod 1000000009的值
输入
第一行n
第二行n个数
输出
一个数:答案
样例输入
3
4 6 2
样例输出
384
样例解释
gcd(4,6,2)∗gcd(4,6)∗gcd(6,2)∗gcd(4)∗gcd(6)∗gcd(2) =384
题解
比赛时没什么头绪而官方题解也不知道什么东西
p.s官方题解是什么分解质因数加笛卡尔树
所以我就暴力+优化吧
暴力:
从1到n做,做到第i,从右往左枚举i之前的每个序列,并gcd乘起来
如图
右边的方框是当前的数,前面的是gcd起来的数,将右边与左边gcd然后乘起来即可。
优化:
假设把左边的存到数组里b[]。图中第一行为b[1],第二行为b[2],……
b 从右往左是单调不增的,而且会出现相同的情况,那么把相同的合并,乘的时候快速幂,就行了。
具体会有多块呢?
在合并后就是递减的,而且没有相等,那最小每次/2,因为最小的素数是2,所以最多就只有
log2(n)
个,时间复杂度就是
O(nlog2n)
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define N 50100
#define mo 1000000009
using namespace std;
ll n,a[N],b[N][4];
ll gcd(ll m,ll n)
{
ll r=m;
while (r!=0){
r=m%n;m=n;n=r;
}
return m;
}
ll mi(ll a,ll b)
{
if(b==0) return 1;if (b==1) return a;
ll k=mi(a,b/2);
if (b%2==0) return k*k%mo;
else return k*k%mo*a%mo;
}
int main()
{
freopen("ned.in","r",stdin);
freopen("ned.out","w",stdout);
scanf("%lld",&n);ll ans=1;
scanf("%lld",&a[1]);ans=a[1];b[1][1]=a[1];b[1][2]=1;b[1][3]=0;
fo(i,2,n)
{
scanf("%d",&a[i]);b[i][3]=i-1;b[i][1]=a[i];b[i][2]=1;
ans*=a[i];ans%=mo;ll k=i;
for(ll j=i-1;j;j=b[j][3])
{
if (b[j][1]==0) continue;
b[j][1]=gcd(a[i],b[j][1]);
ans*=mi(b[j][1],b[j][2]);ans%=mo;
if (b[j][1]==b[k][1]){
b[j][2]+=b[k][2];b[k][3]=j;b[k][1]=0;
}
k=j;
}
}
printf("%lld",ans);
}