hdu4947 GCD Array

来源:互联网 发布:库里2017年总决赛数据 编辑:程序博客网 时间:2024/05/29 08:14

原题网址:http://acm.hdu.edu.cn/showproblem.php?pid=4947
原题大意:对于一组初始为0的数列,进行两种操作:对所有x满足gcd(x,n)=dax增加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 longconst 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;}
原创粉丝点击