HDU 6102 GCDispower(容斥原理+数论+树状数组)

201 篇文章 10 订阅
190 篇文章 1 订阅

Description

给出一个 1 ~N的排列 P M次查询,每次查询给出一个区间 [L,R] ,求 i=LRj=i+1Rk=j+1R[gcd(P[i],P[j])=P[k]]P[k]

Input

第一行一整数 T 表示用例组数,每组用例首先输入两个证书N,M表示排列长度和查询数,之后输入一个 1 ~N的排列 P[i] ,最后 M 行每行输入两个整数L,R表示一个查询 (1T100,1N,M100000,1LRN)

Output

对于每次查询,输出查询结果

Sample Input

2
3 1
3 2 1
1 3
6 3
6 5 4 3 2 1
1 6
2 6
3 5

Sample Output

1
8
5
0

Solution

离线,固定右端点R,维护每一个左端点L的答案,对于当前加入的点 P[R] ,找到所有下标小于 R P[R]的倍数,把这些 P[R] 的倍数全除以 P[R] ,那么对于这些倍数中每一对满足 i<j,gcd(P[i],P[j])=1 ,其对所有左端点 L[1,i] 的查询的答案贡献均为 P[R] ,因此对于每一个 P[R] 的倍数 P[i] ,其对查询左端点在 [1,i] 的答案的贡献为满足 ij<R 中与 P[i] 互素的个数乘以 P[R] ,与 P[i] 互素的数的个数在从右往左扫倍数的时候对素因子容斥得到,对于答案的贡献用树状数组维护即可,对每个数枚举其倍数,对每个倍数要在树状数组中进行两次端点处的更新(前缀和优化区间更新),总时间复杂度 O(nlog2n)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
namespace fastIO 
{
    #define BUF_SIZE 100000
    //fread -> read
    bool IOerror=0;
    inline char nc() 
    {
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if(p1==pend) 
        {
            p1=buf;
            pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if(pend==p1) 
            {
                IOerror=1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) 
    {
        return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
    }
    inline void read(int &x) 
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
    }
    inline void readc(char &x)
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        x=ch;
    }
    #undef BUF_SIZE
};
using namespace fastIO;
typedef long long ll;
typedef pair<int,int>P;
const int maxn=100001;
int T,n,m,p[maxn],pos[maxn],num[maxn],temp[maxn];
ll ans[maxn];
vector<P>fact[maxn],q[maxn];
vector<int>pri[maxn];
int mark[maxn];
struct BIT
{
    #define lowbit(x) (x&(-x))
    ll b[maxn];
    void init()
    {
        memset(b,0,sizeof(b));
    }
    void add(int x,ll v)
    {
        while(x<=n)
            b[x]+=v,x+=lowbit(x);
    }
    ll sum(int x)
    {
        ll ans=0;
        while(x)
            ans+=b[x],x-=lowbit(x);
        return ans;
    }
}bit;
void init(int n=100000)
{
    for(int i=2;i<=n;i++)
        if(!mark[i])
            for(int j=i;j<=n;j+=i)
                mark[j]=1,pri[j].push_back(i);
    for(int i=1;i<=n;i++)
    {   
        int N=1<<pri[i].size();
        for(int j=0;j<N;j++)
        {
            fact[i].push_back(P(1,1));
            for(int k=0;k<pri[i].size();k++)
                if(j&(1<<k))
                    fact[i][j].first*=pri[i][k],fact[i][j].second*=-1;
        }
    } 
}
ll Count(int x)
{
    ll ans=0;
    for(int i=0;i<fact[x].size();i++)
        ans+=num[fact[x][i].first]*fact[x][i].second;
    return ans;
}
void Deal(int x,int v)
{
    for(int i=0;i<fact[x].size();i++)
        num[fact[x][i].first]+=v;
}
int main()
{
    init();
    read(T);
    //scanf("%d",&T);
    while(T--)
    {
        bit.init();
        //scanf("%d%d",&n,&m);
        read(n),read(m);
        for(int i=1;i<=n;i++)
        {
            read(p[i]);
            //scanf("%d",&p[i]);
            pos[p[i]]=i,q[i].clear();
        }
        for(int i=1;i<=m;i++)
        {
            int l,r;
            read(l),read(r);
            //scanf("%d%d",&l,&r);
            q[r].push_back(P(l,i));
        }
        for(int i=1;i<=n;i++)
        {
            int res=0;
            for(int j=2*p[i];j<=n;j+=p[i])
                if(pos[j]<i)temp[++res]=pos[j];
            sort(temp+1,temp+res+1);
            for(int j=res;j>=1;j--)
            {
                ll sum=Count(p[temp[j]]/p[i])*p[i];
                bit.add(1,sum),bit.add(temp[j]+1,-sum);
                Deal(p[temp[j]]/p[i],1);
            }
            for(int j=1;j<=res;j++)Deal(p[temp[j]]/p[i],-1);
            for(int j=0;j<q[i].size();j++)
                ans[q[i][j].second]=bit.sum(q[i][j].first);
        }
        for(int i=1;i<=m;i++)printf("%I64d\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值