莫比乌斯反演 hdu 4676 Sum Of Gcd

Sum Of Gcd

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 513    Accepted Submission(s): 222


Problem Description
Given you a sequence of number a 1, a 2, ..., a n, which is a permutation of 1...n.
You need to answer some queries, each with the following format:
Give you two numbers L, R, you should calculate sum of gcd(a[i], a[j]) for every L <= i < j <= R.
 

Input
First line contains a number T(T <= 10),denote the number of test cases.
Then follow T test cases.
For each test cases,the first line contains a number n(1<=n<= 20000).
The second line contains n number a 1,a 2,...,a n.
The third line contains a number Q(1<=Q<=20000) denoting the number of queries.
Then Q lines follows,each lines contains two integer L,R(1<=L<=R<=n),denote a query.
 

Output
For each case, first you should print "Case #x:", where x indicates the case number between 1 and T.
Then for each query print the answer in one line.
 

Sample Input
  
1 5 3 2 5 4 1 3 1 5 2 4 3 3
 

Sample Output
  
Case #1: 11 4 0
 

Source



题意:有一个数列,是1~n的排列。然后有Q个询问,询问[L,R]中所有gcd(a[i],a[j]) i<j 之和。


思路:


代码:

#include <iostream>
#include <cstdio>
#include <string.h>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
#define LL long long
#define clr(a,x) memset(a,x,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mp make_pair
#define Fout(file) freopen(file,"w",stdout)
#define Fin(file) freopen(file,"r",stdin)
const int maxn=20000+5;
int n,Q;
struct Point
{
    int x,y,id;
    Point(int x=0,int y=0,int id=0)
        :x(x),y(y),id(id) { }
    bool operator<(const Point&a) const
    {
        if(a.x==x) return y>a.y;
        return x<a.x;
    }
}pt[maxn],cpy[maxn];

struct SegmentTree
{
    int minimal[maxn<<2],id[maxn<<2];
    int tree_size;
    void PushUp(int rt)
    {
        if(minimal[rt<<1]>minimal[rt<<1|1]) minimal[rt]=minimal[rt<<1|1],id[rt]=id[rt<<1|1];
        else minimal[rt]=minimal[rt<<1],id[rt]=id[rt<<1];
    }
    void init() { clr(minimal,0x3f); clr(id,-1); }
    void update(int p,int x,int id)
    {
        p+=tree_size; if(minimal[p]<=x) return;
        minimal[p]=x; this->id[p]=id;
        p>>=1;
        while(p>=1) {
            PushUp(p);
            p>>=1;
        }
    }
    int query(int l,int r)
    {
        l+=tree_size; r+=tree_size;
        int ret=-1;
        while(l<=r) {
            if(l&1) {
                if(id[l]!=-1) {
                    if(ret==-1) ret=id[l];
                    else if(pt[ret].x+pt[ret].y>pt[id[l]].x+pt[id[l]].y)
                        ret=id[l];
                }
                ++l;
            }
            if(~r&1) {
                if(id[r]!=-1) {
                    if(ret==-1) ret=id[r];
                    else if(pt[ret].x+pt[ret].y>pt[id[r]].x+pt[id[r]].y)
                        ret=id[r];
                }
                --r;
            }
            l>>=1; r>>=1;
        }
        return ret;
    }
}segtree;

struct Edge
{
    int u,v,cost;
    Edge(int u=0,int v=0,int cost=0)
        :u(u),v(v),cost(cost) { }
    bool operator<(const Edge&e) const { return cost<e.cost; }
}edges[maxn<<3];
int e_cnt;

struct Node
{
    int v;
    Node *next;
}*first[maxn],edge[maxn<<1];
int ptr;

void add(int u,int v)
{
    edge[++ptr].v=v;
    edge[ptr].next=first[u];
    first[u]=&edge[ptr];
}


int a[maxn],b[maxn];
bool cmp_a(int x,int y) { return a[x]<a[y]; }

int Bin(int x,int l,int r)
{
    int m;
    while(l<=r) {
        m=(l+r)>>1;
        if(a[b[m]]==x) return m;
        else if(a[b[m]]<x) l=m+1;
        else r=m-1;
    }
    return l;
}

int find(int x)
{
    if(x==b[x]) return x;
    return b[x]=find(b[x]);
}

void swap(int&a,int&b) { a^=b; b^=a; a^=b; }
void MST(int n)
{
    e_cnt=0;
    for(int dir=0;dir<4;++dir) {
        if(dir==1||dir==3) for(int i=0;i<n;++i) swap(pt[i].x,pt[i].y);
        else if(dir==2) for(int i=0;i<n;++i) pt[i].x=-pt[i].x;
        sort(pt,pt+n);
        segtree.init();
        for(int i=0;i<n;++i) a[i]=pt[i].y-pt[i].x,b[i]=i;
        sort(b,b+n,cmp_a);
        int c=1;
        for(int i=1;i<n;++i)
        if(a[b[i]]!=a[b[i-1]]) b[c++]=b[i];
        int & sz=segtree.tree_size;
        sz=1; while(sz<c) sz<<=1; --sz;
        for(int i=n-1;i>=0;--i) {
            int h=Bin(pt[i].y-pt[i].x,0,c-1)+1;
            int x=segtree.query(h,c);
            if(x!=-1) edges[e_cnt++]=Edge(pt[i].id,pt[x].id,pt[x].y+pt[x].x-pt[i].x-pt[i].y);
            segtree.update(h,pt[i].x+pt[i].y,i);
        }
    }
    sort(edges,edges+e_cnt);
    for(int i=0;i<n;++i) b[i]=i;
    clr(first,0); ptr=0;
    int len=0; int rest=n-1;
    for(int i=0;i<e_cnt;++i) {
        Edge&e=edges[i];
        if(find(e.u)==find(e.v)) continue;
        b[find(e.u)]=find(e.v);
        add(e.u,e.v); add(e.v,e.u);
        len+=e.cost;
        if(--rest==0) break;
    }
}

LL u[maxn],w[maxn];
int factor[maxn][85],sz[maxn];

void divide_factor(int x)
{
    sz[x]=0;
    for(LL i=1;i*i<=x;++i) {
        if(x%i) continue;
        factor[x][sz[x]++]=x/i;
        if(i!=x/i) factor[x][sz[x]++]=i;
    }
}

int cal_mu(int x)
{
    int odd=1;
    for(LL i=2;i*i<=x;++i) {
        int cnt=0;
        while(x%i==0) {
            x/=i;
            ++cnt;
        }
        if(cnt>1) return 0;
        else if(cnt==1) odd=-odd;
    }
    if(x>1) odd=-odd;
    return odd;
}

void pre_init()
{
    u[1]=1;
    factor[1][0]=1; sz[1]=1;
    for(int i=2;i<=20000;++i) {
        divide_factor(i);
        u[i]=cal_mu(i);
    }
    for(int i=1;i<=20000;++i) {
        w[i]=0;
        for(int j=0;j<sz[i];++j) {
            w[i]+=u[factor[i][j]]*i/factor[i][j];
        }
    }
}

int val[maxn];
char ch;
void read_int(int & x)
{
    ch=getchar(); x=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while('0'<=ch&&ch<='9') x=10*x+ch-'0',ch=getchar();
}

LL num[maxn];
LL ans;
LL output[maxn];
void update(int x,LL sign)
{
    for(int i=0;i<sz[x];++i) {
        int y=factor[x][i];
        ans=ans+sign*num[y]*w[y];
        if(sign<0) ans+=w[y];
        num[y]+=sign;
    }
}

void input()
{
    scanf("%d",&n);
    for(int i=0;i<n;++i) read_int(val[i]);
    scanf("%d",&Q);
    for(int i=0;i<Q;++i) {
        read_int(pt[i].x); read_int(pt[i].y);
        pt[i].id=i; cpy[i]=pt[i]; --cpy[i].x; --cpy[i].y;
    }
}

void dfs(int id,int l,int r,int fa)
{
    int L=pt[id].x,R=pt[id].y;
    int l0=l,r0=r;
    while(r0<R) { ++r0; update(val[r0],1); }
    while(r0>R) {  update(val[r0],-1); --r0; }
    while(l0>L) { --l0; update(val[l0],1); }
    while(l0<L) { update(val[l0],-1); ++l0; }
    output[id]=ans;
    for(Node*p=first[id];p;p=p->next) {
        int nxt=p->v;
        if(nxt==fa) continue;
        dfs(nxt,l0,r0,id);
    }
    while(r0<r) { ++r0; update(val[r0],1); }
    while(r0>r) {  update(val[r0],-1); --r0; }
    while(l0>l) { --l0; update(val[l0],1); }
    while(l0<l) { update(val[l0],-1); ++l0; }
}

void solve()
{
    for(int i=0;i<Q;++i) pt[i]=cpy[i];
    dfs(0,pt[0].x,pt[0].x-1,-1);
    for(int i=0;i<Q;++i)
        printf("%I64d\n",output[i]);
}

int main()
{
    pre_init();
    int T;cin>>T;
    for(int Cas=1;Cas<=T;++Cas) {
        ans=0;
        input();
        MST(Q);
        printf("Case #%d:\n",Cas);
        solve();
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值