hdu5381 The sum of gcd]莫队算法

题意:http://acm.hdu.edu.cn/showproblem.php?pid=5381

思路:这个题属于没有修改的区间查询问题,可以用莫队算法来做。首先预处理出每个点以它为起点向左和向右连续一段的gcd发生变化的每个位置,不难发现对每个点A[i],这样的位置最多logA[i]个,这可以利用ST表用nlognlogA[i]的时间预处理,然后用二分+RMQ在nlogn的时间内得到。然后就是区间变化为1时的转移了,不难发现区间变化为1时,变化的答案仅仅是以变化的那一个点作为左端点或右端点的连续子串的gcd的和,而这个gcd最多logA[i]种,利用前面的预处理可以在logA[i]的时间内累加得到答案。总复杂度O(NlogNlogA[i]+N√NlogA[i])

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define X                   first
#define Y                   second
#define pb                  push_back
#define mp                  make_pair
#define all(a)              (a).begin(), (a).end()
#define fillchar(a, x)      memset(a, x, sizeof(a))
#define copy(a, b)          memcpy(a, b, sizeof(a))

typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;

//#ifndef ONLINE_JUDGE
void RI(vector<int>&a,int n){a.resize(n);for(int i=0;i<n;i++)scanf("%d",&a[i]);}
void RI(){}void RI(int&X){scanf("%d",&X);}template<typename...R>
void RI(int&f,R&...r){RI(f);RI(r...);}void RI(int*p,int*q){int d=p<q?1:-1;
while(p!=q){scanf("%d",p);p+=d;}}void print(){cout<<endl;}template<typename T>
void print(const T t){cout<<t<<endl;}template<typename F,typename...R>
void print(const F f,const R...r){cout<<f<<", ";print(r...);}template<typename T>
void print(T*p, T*q){int d=p<q?1:-1;while(p!=q){cout<<*p<<", ";p+=d;}cout<<endl;}
//#endif
template<typename T>bool umax(T&a, const T&b){return b<=a?false:(a=b,true);}
template<typename T>bool umin(T&a, const T&b){return b>=a?false:(a=b,true);}

const double PI = acos(-1.0);
const int INF = 1e9 + 7;
const double EPS = 1e-8;

/* -------------------------------------------------------------------------------- */

const int maxn = 1e4 + 7;

int gcd(int a, int b) {
    return b? gcd(b, a % b) : a;
}

struct ST {
    int dp[maxn][20];
    int index[maxn];
    void init_index() {
        index[1] = 0;
        for (int i = 2; i < maxn; i ++) {
            index[i] = index[i - 1];
            if (!(i & (i - 1))) index[i] ++;
        }
    }
    void init_gcd(int a[], int n) {
        for (int i = 0; i < n; i ++) dp[i][0] = a[i];
        for (int j = 1; (1 << j) <= n; j ++) {
            for (int i = 0; i + (1 << j) - 1 < n; i ++) {
                dp[i][j] = gcd(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
            }
        }
    }

    int query_gcd(int L, int R) {
        int p = index[R - L + 1];
        return gcd(dp[L][p], dp[R - (1 << p) + 1][p]);
    }
};
ST st;

int n, q, block;
int a[maxn];
vector<int> L[maxn], R[maxn];
pair<pii, int> b[maxn];

bool cmp(const pair<pii, int> &a, const pair<pii, int> &b) {
    int lb = a.X.X / block, rb = b.X.X / block;
    return lb == rb? a.X.Y < b.X.Y : lb < rb;
}

void init() {
    for (int i = 0; i < n; i ++) {
        L[i].clear();
        R[i].clear();
    }
    for (int i = 0; i < n; i ++) {
        int u = i;
        R[i].pb(i - 1);
        while (u < n) {
            int l = u, r = n - 1;
            while (l < r) {
                int m = (l + r + 1) >> 1;
                if (st.query_gcd(i, m) == st.query_gcd(i, u)) l = m;
                else r = m - 1;
            }
            u = l + 1;
            R[i].pb(l);
        }
    }
    for (int i = 0; i < n; i ++) {
        int u = i;
        L[i].pb(i + 1);
        while (u >= 0) {
            int l = 0, r = u;
            while (l < r) {
                int m = (l + r) >> 1;
                if (st.query_gcd(m, i) == st.query_gcd(u, i)) r = m;
                else l = m + 1;
            }
            u = l - 1;
            L[i].pb(l);
        }
    }
}

ll f(int l, int r) {
    ll ans = 0;
    for (int i = 1; i < R[l].size(); i ++) {
        if (r <= R[l][i]) return ans + (ll)(r - R[l][i - 1]) * st.query_gcd(l, r);
        ans += (ll)(R[l][i] - R[l][i - 1]) * st.query_gcd(l, R[l][i]);
    }
}

ll g(int l, int r) {
    ll ans = 0;
    for (int i = 1; i < L[r].size(); i ++) {
        if (l >= L[r][i]) return ans + (ll)(L[r][i - 1] - l) * st.query_gcd(l, r);
        ans += (ll)(L[r][i - 1] - L[r][i]) * st.query_gcd(L[r][i], r);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    int T;
    cin >> T;
    st.init_index();
    while (T --) {
        cin >> n;
        block = (int)sqrt(n + 0.1);
        for (int i = 0; i < n; i ++) {
            scanf("%d", a + i);
        }
        st.init_gcd(a, n);
        init();
        cin >> q;
        for (int i = 0; i < q; i ++) {
            scanf("%d%d", &b[i].X.X, &b[i].X.Y);
            b[i].X.X --;
            b[i].X.Y --;
            b[i].Y = i;
        }
        sort(b, b + q, cmp);
        vector<ll> ans(q);
        ll lastans = a[0];
        int lastl = 0, lastr = 0;
        /** 注意区间变化的顺序,优先考虑扩大区间,保证任何时刻区间不为负 */
        for (int i = 0; i < q; i ++) {
            while (lastl > b[i].X.X) {
                lastl --;
                lastans += f(lastl, lastr);
            }
            while (lastr < b[i].X.Y) {
                lastr ++;
                lastans += g(lastl, lastr);
            }
            while (lastl < b[i].X.X) {
                lastans -= f(lastl, lastr);
                lastl ++;
            }
            while (lastr > b[i].X.Y) {
                lastans -= g(lastl, lastr);
                lastr --;
            }
            ans[b[i].Y] = lastans;
        }
        for (int i = 0; i < q; i ++) {
            printf("%I64d\n", ans[i]);
        }
    }
    return 0;
}
时间: 2024-10-12 17:27:34

hdu5381 The sum of gcd]莫队算法的相关文章

HDOJ 5381 The sum of gcd 莫队算法

大神题解: http://blog.csdn.net/u014800748/article/details/47680899 The sum of gcd Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 526    Accepted Submission(s): 226 Problem Description You have an

hdu 5381 The sum of gcd 莫队+预处理

The sum of gcd Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description You have an array A,the length of A is nLet f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj) Input There are multiple test cases. The first line

HDU 5381 The sum of gcd 莫队暴力

链接 题解链接:http://www.cygmasot.com/index.php/2015/08/15/hdu_5381/ 题意: 给定n长的序列 下面n个数给出这个序列 m个询问 下面m行给出询问的区间. 对于一个询问,输出这个区间内的任意子段的gcd 和. 思路: 因为一个数的gcd只会不变或下降,下降一次至少减半,下降至多32次,所以处理出每个数连续相同的gcd的区间. 然后暴力跑莫队. #pragma comment(linker, "/STACK:1024000000,1024000

(预处理+莫队算法)HDU - 5381 The sum of gcd

题意: 一个长度为n的数列,m次查询L到R之间所有连续子序列的gcd之和. 分析: 很明显的莫队算法. 很明显发现了gcd是单调递减的,并且最多存在32个的性质. 想了很久,脑补了许多种方法来拉伸L和R,但是都有漏洞. 实际上,这道题还是比较复杂的.. 在思考的过程中,我没有充分利用gcd的递减性质. 这题其实这题有共通之处,至少在我的做法上是这样的. 可以发现,在R向右拉伸的过程中,增加的和只是从L到R+1中的每一个后缀的和. 向左则为减,L的移动同理. 那么我们只要提前预处理每个位置的前缀所

【BZOJ3781、2038】莫队算法2水题

[BZOJ3781]小B的询问 题意:有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数 题解:初学莫队算法,差不多明白了用莫队的情况,对于这种离线的,区间长度+1时可O(1)修改答案的题,运用莫队算法是最水的 将n分成sqrt(n)块,将询问按照左端点所在的块为第一关键字,右端点的具体位置为第二关键字排序,然后用指针l,r不断暴力平移到询问的左右端点处,

Hdu5145NPY and girls莫队算法

Problem Description NPY's girlfriend blew him out!His honey doesn't love him any more!However, he has so many girlfriend candidates.Because there are too many girls and for the convenience of management, NPY numbered the girls from 1 to n.These girls

数据结构(莫队算法):国家集训队2010 小Z的袜子

[题目描述] 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子.当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择. [

【莫队算法】

·通过排序巧妙优化复杂度,NOIP前的最后一丝宁静. ·目前的题型概括为三种:普通莫队,树形莫队以及带修莫队. 若谈及入门,那么BZOJ2038的美妙袜子一题堪称经典. [例题一]袜子 ·题目大意:      进行区间询问[l,r],输出该区间内随机抽两次抽到相同颜色袜子的概率. ·分析:      首先考虑对于一个长度为n区间内的答案如何求解.题目要求Ans使用最简分数表示:那么分母就是n*n(表示两两袜子之间的随机组合),分子是一个累加和,累加的内容是该区间内每种颜色i出现次数sum[i]的

【算法】莫队算法初探

[算法介绍] 莫队算法是用于离线处理处理区间问题的一类算法,非常易于理解和上手,应用面十分广泛,甚至还可以在树上进行操作. 当我们得到$[L,R]$的答案之后,如果能够以较低的复杂度扩展得到$[L-1,R],[L+1,R],[L,R-1],[L,R+1]$的答案,我们就可以使用莫队算法,通常这个扩展的复杂度是$O(1)$或$O(logn)$. 如果我们对于每个询问都暴力移动左右端点,那么复杂度肯定是$O(n^2)$的,而莫队算法的精髓就在于结合了分块的思想. 设扩展一次的复杂度为$O(f(n))