HDU 6102 GCDispower(莫比乌斯反演)

HDU 6102 GCDispower

原题连接
http://acm.hdu.edu.cn/showproblem.php?pid=6102

给定数组 P[]
m 次询问。每次给定[L,R]计算
Li<j<kR[gcd(Pi,Pj)=Pk]Pk
我们记
G(L,R)=Li<j<kR[gcd(Pi,Pj)=Pk]Pk
n 个数

对于所有的L[1,c] , c[1,n]

A[n][c] 为 : i=c,k=n,j[c+1,n1] , gcd(Pi,Pj)=Pk G(L,n) 的贡献。
我们认为:
A[n][n+t]=0,t>=0
则有:
G(L,R)=t=1Rk=LRA[t][k]=t=1Rk=LtA[t][k]
我们可以用 O(nlog2Pi) 的时间计算出来 A[i][]
当: gcd(Pi,Pj)=Pk 时,则: PiPkPjPk
那么对于 A[i][k]
区间 [i+1,k1] 上,等于 ayPk 的数组成了一个序列。
我们记 f(n) 为此时 a[] 中,与 PiPk 的最大公约数为 n 的元素个数。

F(n)为此时 a[] 中,与 PiPk 的最大公约数为 n 的倍数的元素个数。

那么:
A[i][k]=f(1)Pk

则:

F(n)=n|df(d)

f(n)=n|dμ(dn)F(d)

所以:

f(1)=d>=1u(d)F(d)

dPiPk 时 , F(d)=f(d)=0
所以:

f(1)=d|PiPkμ(d)F(d)

d|PiPk 时, F(d)=(a[]d)
所以:

A[i][k]=Pkd|PiPk(μ(d)(a[]d))

计算一个 A[i][k] 需要 O(log2Pk)
Pi Pk 倍数的时候才会贡献。
所以。需要计算 A[][k] 的时间为 nPk 个。
总时间为
i=1nO(nlog2Pi)<O(nlog3)
A 只需要开一维。我们将A中的历史贡献维护进来.
离线存储询问。在计算 A <script type="math/tex" id="MathJax-Element-49">A</script>的过程中。回答对应的询问。

下面是代码:

注意:实现的过程会有一些细节。大家要想清楚
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <vector>
#define MAXN 110005
using namespace std;
typedef long long LL;

struct arry//树状数组
{
    LL A[MAXN];
    int n;
    void clear()
    {
        fill(A,A+n+5,0);
    }
    arry()
    {
        memset(A,0,sizeof A);
        n=0;
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void add(int x,LL key)
    {
        while(x<=n)
        {
            A[x]+=key;
            x+=lowbit(x);
        }
    }
    LL sum(int x)//查询前x个元素
    {
        LL tmp=0;
        while(x>0)
        {
            tmp+=A[x];
            x-=lowbit(x);
        }
        return tmp;
    }
    LL get(int L,int R)
    {
        return sum(R)-sum(L-1);
    }
}A;

struct pai
{
    int L,R,id;
    pai(int L,int R,int id):L(L),R(R),id(id){}
    pai()
    {
        *this=pai(0,0,0);
    }
    bool operator <(const pai& A)const
    {
        return R<A.R;
    }
}Q[MAXN];//离线问题

void init();//初始化,计算莫比乌斯函数 与 区间因式分解
vector<int>V[MAXN];// 用于存储 因式分解。
int u[MAXN];//莫比乌斯函数

void getMu()
{
    int n=MAXN;
    for(int i=1;i<n;i++)
    {
        int target= i==1 ? 1 : 0;
        int delta = target-u[i];
        u[i]=delta;
        for(int j=i+i;j<n;j+=i) u[j]+=delta;
    }
}

int F[MAXN];
int X[MAXN];
int P[MAXN];
LL ans[MAXN];
int D[MAXN];


int main ()
{
    init();
    int t,n,q,L,R;
    scanf("%d",&t);

    while(t--)
    {
        A.clear();
        scanf("%d %d",&n,&q);
        A.n=n;
        n++;
        for(int i=1;i<n;i++)    scanf("%d",P+i);

        for(int i=1;i<n;i++)    X[P[i]]=i;

        for(int i=0;i<q;i++)
        {
            scanf("%d %d",&L,&R);
            Q[i]=pai(L,R,i);
        }

        sort(Q,Q+q);
        int count=0;
        for(int i=1;i<n;i++)
        {
            int size=n/P[i]+1;
            fill(F,F+size+1,0);
            int deep=0;
            for(int j=P[i];j<n;j+=P[i])
            {
                if(X[j]>i)continue;
                D[deep++]=X[j];
            }

            sort(D,D+deep);
            deep--;
            while((--deep)>-1)
            {
                int x=D[deep];
                int t=P[x]/P[i];
                LL bu=0;
                for(int d=0;d<V[t].size();d++)    bu+=(LL)u[V[t][d]]*F[V[t][d]];
                A.add(x,bu*P[i]);
                //printf("%lld\n",bu*P[i]);
                for(int d=0;d<V[t].size();d++)  F[V[t][d]]++;
            }
            while(Q[count].R==i&&count<q)
            {
                ans[Q[count].id]=A.get(Q[count].L,Q[count].R);
                count++;
            }
        }

        for(int i=0;i<q;i++)    printf("%lld\n",ans[i]);

    }
    return 0;
}

void init()
{
    getMu();
    for(int i=1;i<MAXN;i++)
        for(int j=i;j<MAXN;j+=i)
            V[j].push_back(i);
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值