HDU-6102 GCDispower(莫比乌斯函数+树状数组)

传送门:HDU-6102

题解:莫比乌斯函数+树状数组+离线操作

先对查询按R升序排序,从左到右枚举Ak,再枚举A1~Ak-1中Ak的倍数(因为是1~n的全排列,因此总枚举量为nlogn),得到B数组,将B数组中的数都除以Ak,并按照A中的下标从小到大排序,然后只要求B中所有GCD(Bi,Bj)==1(i<j)的二元组个数即可。

求二元组个数可以直接用莫比乌斯函数求得,对于Bi对答案的贡献为cnt[下标大于i且与Bi互质的Bj]*Ak

每次枚举Ak都要更新A1~Ak-1的贡献,因此可以用树状数组维护。总复杂度O(nlog^2n)

#include<bits/stdc++.h>
#define FIN freopen("in.txt","r",stdin);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MX = 1e5 + 5;
const int INF = 0x3f3f3f3f;
struct Tree {
    int n;
    vector <LL> T ;
    void init (int sz) {
        T.clear();
        n = sz;
        T.resize(n + 1);
    }
    void add (int x, LL v) {
        for (int i = x; i <= n; i += i & -i) T[i] += v;
    }
    LL sum (int x) {
        if (x > n) x = n;
        LL ret = 0;
        for (int i = x; i > 0; i -= i & -i) ret += T[i];
        return ret;
    }
} te;
struct Query {
    int l, r, id;
    bool operator<(const Query& _A)const {
        if (r != _A.r) return r < _A.r;
        return l < _A.l;
    }
} que[MX];
struct Array {
    int val, index;
    bool operator<(const Array& _A)const {
        return index < _A.index;
    }
} A[MX];
int sz, vis[MX], arr[MX];


bool prime[MX];
int mob[MX], p[MX];
vector<int> d[MX];
int num[MX];
void Mobius() {
    int pnum = 0;
    memset(prime, true, sizeof(prime));
    mob[1] = 1;
    for(int i = 2; i < MX; i++) {
        if(prime[i]) {
            p[pnum ++] = i;
            mob[i] = -1;
        }
        for(int j = 0; j < pnum && (LL)i * p[j] < MX; j++) {
            prime[i * p[j]] = false;
            if(i % p[j] == 0) {
                mob[i * p[j]] = 0;
                break;
            }
            mob[i * p[j]] = -mob[i];
        }
    }
}
void presolve() {
    for (int i = 1; i < MX; i++) {
        for (int j = i; j < MX; j += i) {
            d[j].push_back(i);
        }
    }
}



void solve(int k) { //A数组是Aj,B数组是Ai,i<j
    for (int i = 1; i <= sz; i++) {
        int t = A[i].val;
        for (int j = 0; j < d[t].size(); j++) num[d[t][j]]++;
    }
    for (int i = 1; i <= sz; i++) {
        LL cnt = 0;
        int t = A[i].val;
        for (int j = 0; j < d[t].size(); j++) num[d[t][j]]--;
        for (int j = 0; j < d[t].size(); j++)
            cnt += num[d[t][j]] * mob[d[t][j]];
        te.add(A[i].index, cnt * k);
    }
}

LL ans[MX];
int main() {
    //FIN;
    Mobius(); presolve();
    int T, n, m;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        te.init(n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &arr[i]);
            vis[i] = 0;
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d%d", &que[i].l, &que[i].r);
            que[i].id = i;
        }
        sort(que + 1, que + m + 1);
        vis[arr[1]] = 1; vis[arr[2]] = 2;
        for (int i = 1, k = 3; i <= m; i++) {
            for (; k <= que[i].r; k++) {
                sz = 0;
                vis[arr[k]] = k;
                for (int x = 2; x * arr[k] <= n; x++) {
                    if (vis[x * arr[k]]) {
                        A[++sz].val = x;
                        A[sz].index = vis[x * arr[k]];
                    }
                }
                sort(A + 1, A + sz + 1);
                solve(arr[k]);
            }
            ans[que[i].id] = te.sum(que[i].r) - te.sum(que[i].l - 1);
        }
        for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值