[ZOJ 3800 Calculation] 分段+后缀和+区间和 / 分段+线段树

题目

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5332

分析

这道题目还是很繁琐的,看了题解写了好久才写出来

ST表+Two Pointers+后缀和维护+二分

对于给定的M,记l[i]=min{j|gcd(i,j)=M}, r[i]=max{j| M|a[j]}+1, 如果M不能整除a[i]那么l[i]=r[i]=i,这里l[i]和r[i]可以用two pointers线性解决

那么对于任意x属于[l[i],r[i]),gcd(i,x)=M

设s[i]=r[i]-l[i], 则s[i]表示gcd(i,x)=M的x的个数

将这个数列根据能不能整除M分段,第i段的起点为start[i]

每次查询L,R时,找到L和R所在的段,设为a和b

如果a=b,那么就要找[L,R)中l[x]>=R的第一个x,则答案为sigma(R-l[i]),L<=i<x,可以通过二分找到x,在维护l的后缀和求值

如果a!=b,那么对于(a,b)区间内的,答案为sigma(s[i]),可以通过维护s[i]的后缀和直接得到

对于a段内,类似a=b的情况,相当于找[L,start[a+1])中l[x]>=start[a+1]的第一个x

对于b段内,类似a=b的情况,相当于找[start[b],R)中l[x]>=R的第一个x

一开始想把不能整除的段都去掉,结果发现实现时情况太多,反而不如都留着,一块处理

附上题解原址http://wzimpha.sinaapp.com/archives/670

代码

/**************************************************
 *        Problem:  ZOJ 3800
 *         Author:  clavichord93
 *          State:  Accepted
 **************************************************/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAX_N = 100005;
const int MAX_M = 50;
const int MAX_D = 21;

struct STTable {
    int n;
    int log2[MAX_N];
    int dp[MAX_N][MAX_D];
    void makeLog() {
        log2[0] = 0;
        for (int i = 1; i < MAX_N; i++) {
            log2[i] = log2[i - 1];
            if ((1 << (log2[i] + 1)) == i) {
                log2[i]++;
            }
        }
    }
    void makeST(int *a, int size) {
        n = size;
        for (int i = 0; i < n; i++) {
            dp[i][0] = a[i];
        }
        for (int j = 1; j < MAX_D; j++) {
            for (int i = 0; i < n - (1 << (j - 1)); i++) {
                dp[i][j] = __gcd(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int getAns(int l, int r) {
        int st = log2[r - l + 1];
        return __gcd(dp[l][st], dp[r - (1 << st) + 1][st]);
    }
};

STTable stQuery;

struct Solver {
    int l[MAX_N];
    int r[MAX_N];
    long long s[MAX_N];
    int cntBlock;
    int start[MAX_N];
    long long suffixSumL[MAX_N];
    long long suffixSumSize[MAX_N];

    void make(int *a, int n, int d) {
        memset(l, 0, sizeof(l));
        memset(r, 0, sizeof(r));
        memset(s, 0, sizeof(s));
        memset(start, 0, sizeof(start));
        memset(suffixSumL, 0, sizeof(suffixSumL));
        memset(suffixSumSize, 0, sizeof(suffixSumSize));
        cntBlock = 0;

        // Calculate l[] & r[]
        for (int i = 0, lastL = 0, lastR = 0; i < n; i++) {
            if (a[i] % d != 0) {
                l[i] = i;
                r[i] = i;
                lastL = i + 1;
                lastR = i + 1;
            }
            else {
                while (lastR < n && a[lastR] % d == 0) {
                    lastR++;
                }
                lastL = max(lastL, i);
                while (lastL < lastR && stQuery.getAns(i, lastL) > d) {
                    lastL++;
                }
                l[i] = lastL;
                r[i] = lastR;
            }
        }

        // Calculate s[]
        for (int i = n - 1; i >= 0; i--) {
            if (r[i] > l[i]) {
                s[i] = s[i + 1] + r[i] - l[i];
            }
            else {
                s[i] = 0;
            }
        }

        // Get Groups
        cntBlock = 0;
        for (int i = 0; i < n; i++) {
            start[cntBlock++] = i;
            if (a[i] % d == 0) {
                while (i + 1 < n && a[i + 1] % d == 0) {
                    i++;
                }
            }
            else {
                while (i + 1 < n && a[i + 1] % d != 0) {
                    i++;
                }
            }
        }
        start[cntBlock] = n;

        // Calculate suffixSumL
        for (int i = n - 1; i >= 0; i--) {
            suffixSumL[i] = suffixSumL[i + 1] + l[i];
        }

        // Calculate suffixSumSize
        for (int i = cntBlock - 1; i >= 0; i--) {
            suffixSumSize[i] = suffixSumSize[i + 1] + s[start[i]];
        }

        //for (int i = 0; i < n; i++) {
            //printf("%d ", l[i]);
        //}
        //printf("\n");
        //printf("\n");
    }

    long long query(int queryL, int queryR, int d) {
        int a = upper_bound(start, start + cntBlock, queryL) - start - 1;
        int b = upper_bound(start, start + cntBlock, queryR - 1) - start - 1;
        //printf("A = %d, B = %d\n", a, b);
        if (a == b) {
            if (s[start[a]]) {
                int x = queryL;
                int y = lower_bound(l + queryL, l + start[a + 1], queryR) - l;
                //printf("X = %d, Y = %d\n", x, y);
                return (long long)queryR * (y - x) - (suffixSumL[x] - suffixSumL[y]);
            }
            return 0;
        }
        else {
            #ifdef debug
            printf("A = %d, B = %d\n", a, b);
            for (int i = 0; i < cntBlock; i++) {
                printf("%d ", start[i]);
            }
            printf("\n");
            #endif
            long long ret = suffixSumSize[a + 1] - suffixSumSize[b];
            #ifdef debug
            printf("%lld\n", ret);
            printf("Start = %d\n", start[a]);
            printf("L = %d, R = %d\n", l[start[a]], r[start[a]]);
            printf("S = %lld\n", s[start[a]]);
            #endif
            #ifdef debug
            printf("%lld\n", ret);
            #endif
            if (s[start[a]]) {
                int x = queryL;
                int y = lower_bound(l + queryL, l + start[a + 1], start[a + 1]) - l;
                ret += (long long)start[a + 1] * (y - x) - (suffixSumL[x] - suffixSumL[y]);
            }
            #ifdef debug
            printf("%lld\n", ret);
            #endif
            if (s[start[b]]) {
                int x = start[b];
                int y = lower_bound(l + start[b], l + queryR, queryR) - l;
                ret += (long long)queryR * (y - x) - (suffixSumL[x] - suffixSumL[y]);
            }
            #ifdef debug
            printf("%lld\n", ret);
            #endif
            return ret;
        }
    }
};

int n, m, q;
int a[MAX_N];
int gs[MAX_N];

Solver solver[MAX_M];

int main() {
    #ifdef LOCAL_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    #endif
    stQuery.makeLog();
    while (scanf("%d %d %d", &n, &m, &q) != EOF) {
        for (int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
        }
        for (int i = 0; i < m; i++) {
            scanf("%d", &gs[i]);
        }
        sort(gs, gs + m);

        stQuery.makeST(a, n);
        for (int i = 0; i < m; i++) {
            solver[i].make(a, n, gs[i]);
        }

        for (int i = 0; i < q; i++) {
            int l, r, g;
            scanf("%d %d %d", &l, &r, &g);
            int t = lower_bound(gs, gs + m, g) - gs;
            printf("%lld\n", solver[t].query(l, r, g));
        }
    }

    return 0;
}


-------------------------------------------------另一种姿势-------------------------------------------------

分析

线段树

对于给定的R,gcd(L,R)随L变小而变小,维护gcd(L,R)相等的段,那么段数不会超过a[R]的约数个

于是离线所有询问,枚举R,从左往右扫描,对每一个GS用一棵线段树维护G(L,R)的值

维护gcd(L,R)相等的段,在对应线段树上做区间+1,因为这一段中的i的gcd(i,R)都为该值

然后处理以R结尾的询问,做区间求和即可

感觉比上面的方法容易想也容易实现,效率甚至更好

参见http://blog.csdn.net/qian99/article/details/38827863

代码

/**************************************************
 *        Problem:  ZOJ 3800
 *         Author:  clavichord93
 *          State:  Accepted
 **************************************************/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int MAX_N = 100005;
const int MAX_Q = 100005;
const int MAX_M = 50;

struct Event {
    int l, g, id;
    Event() {}
    Event(int _l, int _g, int _id)
    : l(_l), g(_g), id(_id) {}
};

#define lch(t) (t << 1)
#define rch(t) (t << 1 | 1)

struct SegmentTree {
    int tag[MAX_N << 2];
    long long sum[MAX_N << 2];

    void clear(int n) {
        memset(tag, 0, sizeof(int) * n * 4);
        memset(sum, 0, sizeof(long long) * n * 4);
    }

    void pushdown(int t, int l, int r) {
        if (tag[t]) {
            int lt = lch(t);
            int rt = rch(t);
            int mid = (l + r) >> 1;

            sum[lt] += (long long)tag[t] * (mid - l + 1);
            tag[lt] += tag[t];
            sum[rt] += (long long)tag[t] * (r - mid);
            tag[rt] += tag[t];

            tag[t] = 0;
        }
    }

    void add(int t, int l, int r, int x, int y) {
        if (x <= l && r <= y) {
            tag[t]++;
            sum[t] += r - l + 1;
        }
        else {
            pushdown(t, l, r);
            int mid = (l + r) >> 1;
            if (x <= mid) {
                add(lch(t), l, mid, x, y);
            }
            if (y > mid) {
                add(rch(t), mid + 1, r, x, y);
            }
            sum[t] = sum[lch(t)] + sum[rch(t)];
        }
    }
    
    long long getAns(int t, int l, int r, int x, int y) {
        if (x <= l && r <= y) {
            return sum[t];
        }
        else {
            pushdown(t, l, r);
            int mid = (l + r) >> 1;
            long long ret = 0;
            if (x <= mid) {
                ret += getAns(lch(t), l, mid, x, y);
            }
            if (y > mid) {
                ret += getAns(rch(t), mid + 1, r, x, y);
            }
            return ret;
        }
    }
};

#undef lch
#undef rch

int n, m, q;
int a[MAX_N];
int gs[MAX_M];
int id[MAX_N];
long long ans[MAX_Q];
SegmentTree sgt[MAX_M];
int cntBlock;
int _cntBlock;
pair<int, int> block[MAX_N];
pair<int, int> _block[MAX_N];
vector<Event> queries[MAX_N];

int main() {
    #ifdef LOCAL_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    #endif
    while (scanf("%d %d %d", &n, &m, &q) != EOF) {
        for (int i = 0; i < n; i++) {
            queries[i].clear();
        }
        for (int i = 0; i < m; i++) {
            sgt[i].clear(n);
        }
        memset(id, 0xff, sizeof(id));

        for (int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
        }
        for (int i = 0; i < m; i++) {
            scanf("%d", &gs[i]);
            id[gs[i]] = i;
        }

        for (int i = 0; i < q; i++) {
            int l, r, g;
            scanf("%d %d %d", &l, &r, &g);
            queries[r - 1].push_back(Event(l, g, i));
        }

        cntBlock = 0;
        for (int i = 0; i < n; i++) {
            _cntBlock = 0;
            for (int j = 0; j < cntBlock; j++) {
                block[j].second = __gcd(block[j].second, a[i]);
                if (!j || block[j].second != block[j - 1].second) {
                    _block[_cntBlock++] = block[j];
                }
            }
            if (!_cntBlock || a[i] != _block[_cntBlock - 1].second) {
                _block[_cntBlock++] = make_pair(i, a[i]);
            }
            for (int j = 0; j < _cntBlock; j++) {
                block[j] = _block[j];
            }
            cntBlock = _cntBlock;

            //for (int j = 0; j < cntBlock; j++) {
                //printf("%d %d\n", block[j].first, block[j].second);
            //}
            //printf("\n");

            for (int j = 0; j < cntBlock; j++) {
                int t = id[block[j].second];
                if (t != -1) {
                    if (j == cntBlock - 1) {
                        //cout << "ADD " << t << " " << block[cntBlock - 1].first << " " << i << endl;
                        sgt[t].add(1, 0, n - 1, block[cntBlock - 1].first, i);
                    }
                    else {
                        //cout << "ADD " << t << " " << block[j].first << " " << block[j + 1].first << endl;
                        sgt[t].add(1, 0, n - 1, block[j].first, block[j + 1].first - 1);
                    }
                }
            }

            int cntQuery = queries[i].size();
            for (int j = 0; j < cntQuery; j++) {
                int t = id[queries[i][j].g];
                //cout << "GET " << t << " " << queries[i][j].l << " " << i << endl;
                ans[queries[i][j].id] = sgt[t].getAns(1, 0, n - 1, queries[i][j].l, i);
            }
            //cout << i << endl;
        }

        for (int i = 0; i < q; i++) {
            printf("%lld\n", ans[i]);
        }
    }

    return 0;
}


你可能感兴趣的:([ZOJ 3800 Calculation] 分段+后缀和+区间和 / 分段+线段树)