您的位置:首页 > 其它

[JZOJ4586] Ned 的难题

2016-07-07 16:52 295 查看

Description

给出一个N个数的序列,求其中所有连续区间的最大公约数的乘积



Solution

20%

N2暴力显然

40%

只用在上面的基础上加上一句话,如果搜到的区间的gcd已经是1了就退出

100%

有两种方法,一种是分解质因数乱搞的(我不会)

讲另外一种比较简单的

我们设i,j,i向右枚举,j向左枚举(相当于反过来N2暴力)



那么显然1~i−1的所有区间的gcd都已经知道了

显然往左的gcd是单调不增的

并且我们有一个性质,区间中的值种数至多有logi2 种

为什么呢?

因为每一次求最小公约数,要么是相等,要么除掉了若干个质因数,最少也要除以2

所以我们可以在j向前枚举的时候,碰到相等的区间就可以直接跳过去,再用快速幂求对答案的贡献。

复杂度O(NlogN)

Code

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define mo 1000000009
#define LL long long
using namespace std;
int n;
LL a[50005],ans,last[50005][2];
LL gcd(LL x,LL y)
{
LL r=x%y;
x=y;
y=r;
while (r!=0)
{
r=x%y;
x=y;
y=r;
}
return x;
}
LL ksm(LL k,LL n)
{
if (n==1) return k;
LL s=ksm(k,n/2);
if (n%2) return(s*s%mo*k%mo);
else return (s*s%mo);
}
int main()
{
int i,j;
ans=1;
cin>>n;
fo(i,1,n) scanf("%lld",&a[i]);
ans=a[1];
last[0][1]=1;
last[1][0]=1;
last[1][1]=a[1];
fo(i,2,n)
{
int c=a[i];
last[i][0]=i;
last[i][1]=a[i];
j=i;
while (j>0)
{
int p=j;
j=last[j][0]-1;
while (gcd(c,last[j][1])==c&&j>0) j=last[j][0]-1;
last[p][0]=j+1;
ans=(ans*ksm(c,p-j))%mo;
c=gcd(c,last[j][1]);
}
}
cout<<ans;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: