Ned的难题

题目描述

Ned再也看不下去Robert的种种恶习,于是他决定出一道题来让他醒悟。
Ned的题目是这样:
给出一个有n个数的序列,求其中所有连续子序列的数的最大公因数的乘积模1000000009的值。
Robert当时就立刻给出了一个超级无敌速度无人能及的O(1)错误解法。
既然Robert不会做,Ned决定让你来做出这题来证明Robert不应颓废下去。

输入

第一行一个正整数n(1≤n≤50000)
第二行n个正整数a[i](1≤a[i]≤10000000)

输出
一行一个整数表示序列中所有连续子序列的数的最大公因数的乘积模1000000009的值

样例输入
3
4 6 2

样例输出
384

样例解释
gcd(4,6,2)*gcd(4,6)*gcd(6,2)*gcd(4)*gcd(6)*gcd(2)=2*2*2*4*6*2=384

数据范围

对于20%的数据:
1≤n≤1000

对于额外20%的数据保证不存在长于2000的数列的最大公约数大于一

对于100%的数据:
1≤n≤50000
1≤a[i]≤10000000

【题解】
(题外话:考试时我不记得求一群数的最大公约数怎么弄了。。。下考后发现我智障了:不就是先求两个数的最大公因数,再拿求出的数与第三个数求最大公因数吗!!!)

这里写图片描述
所以某个质数p对答案的贡献跟一个子序列里这个质数的次数的最小值有关,即p^min(a[i]),观察到最终求的是gcd的乘积,所以,我们可以现将所有数分解质因数,然后对于一个质数对某个区间的答案的贡献,其实就是这个区间内这个质数的次数的最小值,所以我们可以用笛卡尔树加上快速幂来解决,时间复杂度O(8nlogn)(注,有常数8是因为每个小于10^7数最多可以被分成八个不同的质因数)。

然而出题人的题解也太高大上了吧。。。笛卡尔树是什么毛线???
于是我就想出了一种鬼畜的解法:

从前往后求1~i序列的最大公约数n,当前搜到了i,若搜到一个数时,序列的最大公约数突然变成n/j,则说明1~i-1中j为该序列的公约数,然后这i-1个数中必定有(i-1)* (i-1-1)/2对公约数中有此数,因此答案可乘上这个数的(i-1)*(i-1-1)/2次方(快速幂),再把这i-1个数除上j即可。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
#define re register
#define il inline
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int mod=1e9+9;
ll n,a[50005]={},ans=1;
ll gcd(ll a,ll b)//求最大公约数
{
    return a==0?b:gcd(b%a,a);
}
ll Pow(ll a,ll b)//快速幂
{
    if(b==1)return a;
    ll s=Pow(a,b>>1);
    s=s*s%mod;
    if(b&1) s=s*a%mod;
    return s;
}
il ll gi()
{  
  re ll x=0;
  re ll t=1;
  re char ch=getchar();
  while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
int main()
{
    freopen("ned.in","r",stdin);
    freopen("ned.out","w",stdout);
    n=gi();
    fp(i,1,n)
        a[i]=gi();
    a[++n]=1;//有一种情况:搜到最后时,gcd不为0,这时未加特判的程序会直接退出,留下公约数没处理
    ll gg,ggg;
    fp(i,1,n)
    {
        if(a[i]==1) continue;
        ll cnt=1;
        gg=a[i];
        fp(j,i+1,n)
        {
            cnt++;
            ggg=gcd(min(gg,a[j]),max(gg,a[j]));
            if(gg!=ggg)
            {
                ll tt=gg/ggg;
                ans=ans*Pow(tt,cnt*(cnt-1)/2)%mod;
                fp(k,i,j-1) a[k]/=tt;
                gg=ggg;
            }
            if(ggg==1) break;//没必要再看了,都互质了
        }
    }
    printf("%lld
",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
原文地址:https://www.cnblogs.com/yanshannan/p/7413151.html