bzoj3560: DZY Loves Math V

这个题不应该想不到的啊

考虑对于每个质因数分别计算答案,最后再乘起来

那么每个答案就是phi(p^(sigema(1~n)i bi))  bi表示这个位置用了多少当前质因数,显然我们可以算出bi的上界ci,即在ai中pi出现了几次

而函数值非常简单,就是p^(k-1)*(p-1),而根据乘法分配律可以得到答案其实就是product(1~n)i (sigema(1~ci)j p^j)*(p-1)/p

因为一个数的因数个数不会超过30,直接暴力算就可以了

还有一个问题,就是phi(1)的情况,我们可以对柿子进行一点操作令它合法,变成((sigema(1~ci)j p^j)-1)*(p-1)/p+1就好了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int _=1e2;
const int maxn=1e5+_;
const int maxp=10+5;
const LL mod=1e9+7;
LL quick_pow(LL A,int p)
{
    LL ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=ret*A%mod;
        A=A*A%mod;p/=2;
    }
    return ret;
}

struct node{int p,c;}p[maxp*maxn];int plen;
bool cmp(node n1,node n2){return n1.p<n2.p;}
void divi(int n,int w)
{
    for(int i=2;i*i<=n;i++)
        if(n%i==0)
        {
            p[++plen].p=i;
            while(n%i==0)n/=i,p[plen].c++;
        }
    if(n!=1)p[++plen].p=n,p[plen].c=1;
}

int a[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        divi(a[i],i);
    }
    sort(p+1,p+plen+1,cmp);
    
    LL ans=1; int l,r=0;
    while(l<=plen)
    {
        l=r+1;
        while(r<plen&&p[r+1].p==p[l].p)r++;
        
        LL num=1;
        for(int i=l;i<=r;i++)
        {
            LL sum=0,d=1;
            for(int j=0;j<=p[i].c;j++)
            {
                sum+=d;if(sum>=mod)sum-=mod;
                d=d*p[l].p%mod;
            }
            num=num*sum%mod;
        }
        num=(num+mod-1)*(p[l].p-1)%mod*quick_pow(p[l].p,mod-2)%mod+1;
                
        ans=ans*num%mod;
    }
    printf("%lld\n",ans);
    
    return 0;
}

 

posted @ 2019-03-25 14:27  AKCqhzdy  阅读(127)  评论(0编辑  收藏  举报