hdu 5381 The sum of gcd (線段樹x樹狀數組x區間和維護進階x離線處理)

題意:
給定區間,求所有子區間gcd之和。區間gcd定義爲區間中所有數的gcd。
思路:
從一個點開始,朝某個方向一直求gcd,會形成一個非增序列,且下降不超過32次。(每次下降至少減半)
所以在線的思路就是用線段樹,每個節點代表一個區間,sum保存這個區間所有子區間的gcd和,同時保存兩個狀態集合,分別是從左端點開始的連續區間,和從右端點開始的連續區間。參考了 Dora 的代碼。

同時,離線的思路是:
sum[i] 存以i爲左端點的所有區間的gcd和。然後從1到n掃一遍,通過前綴和相減就可以求得區間和。設當前掃到ai,需要更新它前面的所有sum。但是新增的區間都是以ai爲右端點的,所以只需要不超過32次區間更新。

在線code

const int N = 10000 + 5;
typedef long long LL;

int gcd(int a, int b) {
    for (int t; b; t = a, a = b, b = t % b);
    return a;
}

struct part {
    int cnt, g;
};

struct node {
    vector<part> L, R;
    LL sum;

    void upd(const node& rhs) {
        sum += rhs.sum;
        for (int i = 0; i < R.size(); ++ i) {
            int ng = R[i].g;
            for (int j = 0; j < rhs.L.size(); ++ j) {
                ng = gcd(ng, rhs.L[j].g);
                sum += (LL)(ng)  * R[i].cnt * rhs.L[j].cnt;
            }
        }

        for (int i = 0; i < rhs.L.size(); ++ i) Insert(L, rhs.L[i]);
        vector<part> tmp(rhs.R);
        for (int i = 0; i < R.size(); ++ i) Insert(tmp, R[i]);
        R.swap(tmp);
    }

    static void Insert(vector<part>& vec, const part& x) {
        int ng = gcd(vec.back().g, x.g);
        if ( ng == vec.back().g ) {
            vec.back().cnt += x.cnt;
        } else {
            vec.push_back( (part) {x.cnt, ng} );
        }
    }
};

node tree[N*4];
#define lc o<<1
#define rc o<<1|1

void print(const node& x, int l, int r) {
    cout << "node "  << l << ' ' << r << endl;
    cout << "  sum: " << x.sum << endl;
    cout << "  L: ";for(auto i:x.L) cout << "(" << i.cnt << " , " << i.g << ") ";cout << endl;
    cout << "  R: ";for(auto i:x.R) cout << "(" << i.cnt << " , " << i.g << ") ";cout << endl;
    cout << endl;
}

void build(int o, int l, int r) {
    if ( l != r ) {
        int m = (l + r) >> 1;
        build(lc, l, m);
        build(rc, m+1, r);
        tree[o] = tree[lc];
        tree[o].upd(tree[rc]);
    } else {
        int x;
        scanf("%d", &x);
        tree[o].sum = x;
        vector<part>().swap(tree[o].L);
        vector<part>().swap(tree[o].R);
        tree[o].L.push_back( (part) { 1, x } );
        tree[o].R.push_back( (part) { 1, x } );
    }
    //print(tree[o], l, r);
}

int qL, qR;
node qres;
void query(int o, int l, int r) {
    if ( qL <= l && r <= qR ) {
        if ( l == qL )
            qres = tree[o];
        else
            qres.upd(tree[o]);
        return;
    }
    int m = (l + r) >> 1;
    if ( qL <= m ) query(lc, l, m);
    if ( qR > m ) query(rc, m+1, r);
}

int main() {
#ifdef _LOCA_ENV_
    freopen("input.in", "r", stdin);
#endif // _LOCA_ENV
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, q;
        scanf("%d", &n);
        build(1, 1, n);
        scanf("%d", &q);
        rep(i, 1, q) {
            scanf("%d%d", &qL, &qR);
            //cout << "query " << qL << ' ' << qR << endl;
            query(1, 1, n);
            printf("%I64d\n", qres.sum);
        }
    }
    return 0;
}
發佈了278 篇原創文章 · 獲贊 10 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章