hdu4947 GCD Array

原题网址:http://acm.hdu.edu.cn/showproblem.php?pid=4947
原题大意:对于一组初始为0的数列,进行两种操作:对所有x满足 gcd(x,n)=d ax 增加v;计算 xi=1ai
对于条件一,其等价于对每个数增加 [gcd(x,n)=d]v ,而 [gcd(x,n)=d]v=[gcd(x/d,n/d)=1]v=i|gcd(x/d,n/d)μ(i)v=i|nd and id|xμ(i)v ,构造数组sum[],满足 a[i]=j|isum[j] 操作一即为对所有满足条件的i,对sum[i*d]加 μ(i)v ,操作二求和则对于前x个 ai 的和,sum[i]被计算的次数为 xi 次,这块可以用分块思想,同时用树状数组维护。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>

using namespace std;
#define ll long long
const int N = 200010;
bool check[N];
int prime[N];
int mu[N];//莫比乌斯函数
ll sum[N];
ll ans, temp, lasttemp, pos;
vector<int> fac[N];//因子

ll getsum(int x){
    ll ans = 0;
    for (; x; x -= x&-x)
        ans += sum[x];
    return ans;
}

void add(int x, int v){
    for (; x<N; x += x&-x)
        sum[x] += v;
}

void Moblus(){//线性筛法求莫比乌斯函数
    memset(check, false, sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for (int i = 2; i <= N; i++){
        if (!check[i]){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < tot; j++){
            if (i*prime[j] > N) break;
            check[i*prime[j]] = true;
            if (i%prime[j] == 0){
                mu[i*prime[j]] = 0;
                break;
            }
            else{
                mu[i*prime[j]] = -mu[i];
            }
        }
    }
}

int main(){
    int l, m, cas = 0;
    Moblus();
    for (int i = 1; i < N; i++)
        for (int j = i; j < N; j += i)
            fac[j].push_back(i);
    while (scanf("%d%d", &l, &m), l, m){
        cas++;
        printf("Case #%d:\n", cas);
        memset(sum, 0, sizeof(sum));
        while (m--){
            int t, n, d, v, x;
            scanf("%d", &t);
            if (t == 1){
                scanf("%d%d%d", &n, &d, &v);
                if (n%d) continue;
                n /= d;
                for (int i = 0; i < fac[n].size(); i++){
                    x = fac[n][i];
                    add(x*d, mu[x] * v);
                }
            }
            else{
                scanf("%d", &x);
                ans = temp = 0;
                for (int i = 1; i <= x; i = pos + 1){
                    pos = x / (x / i);
                    lasttemp = temp;
                    temp = getsum(pos);
                    ans += x / i*(temp - lasttemp);
                }
                printf("%lld\n", ans);
            }
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值