您的位置:首页 > 其它

HDU4676 Sum Of Gcd

2013-08-15 22:07 260 查看
题意:给定一个n的排列,询问L到R之间任意两个数的gcd之和

以下是解题报告的思路,解题报告看了n久才看懂, 记一下,以免以后忘记了~~

对于L到R之间的一个数A,如果数A的一个因子B在L到R之间出现了K次,则结果加上K*F[B]。例如4有因子2,出现了K次,加上K*F[2]。

当然A有因子B,则A有B的因子C,又要加上K*F[C]。 假如A有因子2,除去A之外,L到R还有有1个因子2,1个因子1,其实gcd之和为2。所以要因子要乘上1个系数F[i]。

F[i]怎么求呢,假设B有n个因子,B1, B2...Bn,则F[B] + F[B1] + F[B2] + ... + F[Bn] = B。这样的话同时有K个B1,B2,... Bn时gcd之和就加上了K*B。

F[1]=1,然后可暴力求出F[i]。其实F[i] = phi[i]。

于是统计L到R之间的每个因子出现的次数即可求出答案。

因为询问太多,所以要离线处理每个询问。当把A加进区间时,对A的每个因子求答案,然后改变因子出现次数,删除时差不多。

//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
#include<cstdio>
#include<cstring>
#include<climits>
#include<string>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<queue>
#include<vector>
#include<stack>
#include<map>
#include<set>
#include<stdlib.h>
using namespace std;
typedef long long ll;

void io_in_data() { freopen("data.in", "r", stdin);freopen("data1.out", "w", stdout); }

const int MAXN = 20007;
vector<int> divisor[MAXN];
int f[MAXN], a[MAXN], L[MAXN], R[MAXN], cnt[MAXN], qid[MAXN], M;
ll ans[MAXN];

void getdivisor() {
    int i, j;
    for(i = 1; i < MAXN; i++) for(j = i; j < MAXN; j += i) divisor[j].push_back(i);
}

void getf() {
    for(int i = 1; i < MAXN; i++) {
        int t = i;
        int j, sz = divisor[i].size();
        for(j = 0; j < sz; j++) t -= f[divisor[i][j]];
        f[i] = t;
    }
}

int qcmp(int x, int y) {
    int px = L[x] / M; //避免L相差不大然后R相差很大的情况
    int py = L[y] / M;
    if(px != py) return px < py;
    return R[x] < R[y];
}

ll erase(int x) {
    int i, y, sz = divisor[x].size();
    ll ret = 0;
    for(i = 0; i < sz; i++) {
        y = divisor[x][i];
        ret += 1LL*f[y]*(--cnt[y]);
    }
    return ret;
}

ll insert(int x) {
    int i, y, sz = divisor[x].size();
    ll ret = 0;
    for(i = 0; i < sz; i++) {
        y = divisor[x][i];
        ret += 1LL*f[y]*(cnt[y]++);
    }
    return ret;
}

void solve(int n, int q) {
    int i;
    for(i = 1; i <= q; i++) qid[i] = i;
    M = (int)sqrt(n*1.0);
    sort(qid+1, qid+1+q, qcmp);
    memset(cnt, 0, sizeof(cnt));
    
    int l = 0, r = 0;
    ll now = 0;
    for(i = 1; i <= q; i++) {
        int id = qid[i];
        int nl = L[id], nr = R[id];
        while(l < nl) {
            now -= erase(a[l]);
            l++;
        }
        while(l > nl) {
            l--;
            now += insert(a[l]);
        }
        while(r < nr) {
            r++;
            now += insert(a[r]);
        }
        while(r > nr) {
            now -= erase(a[r]);
            r--;
        }
        ans[id] = now;
    }
}
        
int main() {
    getdivisor();
    getf();
    int T, id = 0;
    scanf("%d", &T);
    while(T--) {
        int i, n, q;
        scanf("%d", &n);
        for(i = 1; i <= n; i++) scanf("%d", a+i);
        scanf("%d", &q);
        for(i = 1; i <= q; i++) scanf("%d%d", L+i, R+i);
        
        solve(n, q);
        
        printf("Case #%d:\n", ++id);
        for(i = 1; i <= q; i++) printf("%I64d\n", ans[i]);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: