hdu 5726 GCD 倍增+ 二分

题目链接

给n个数, 定义一个运算f[l,r] = gcd(al, al+1,....ar)。 然后给你m个询问, 每次询问给出l, r。 求出f[l, r]的值以及有多少对l‘, r‘ 使得f[l, r] = f[l‘, r‘]。

第一个很简单, 用倍增的思想就可以了。

然后是第二个, 我们枚举每一个左端点i, 显然f[i, j]是只降不增的。 那么我们可以二分找到所有使得f[i, j]下降的值j。 因为gcd每次至少变为原来的二分之一, 而ai最大为1e9. 所以最多只有log2(1e9)个这样的点。 我们都找出来然后放到map里就可以了。 具体看代码

#include <bits/stdc++.h>

using namespace std;
#define ll long longint n;
const int maxn = 1e5+5;
int a[maxn], f[maxn][20], mm[maxn];
map <int, ll> mp;
int gcd(int a, int b)
{
    return b?gcd(b, a%b):a;
}
void initrmq()
{
    mm[0] = -1;
    for(int i = 1; i <= n; i++) {
        mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
    }
    for(int j = 1; j < 17; j++) {
        for(int i = 1; i + (1<<j)-1 <= n; i++) {
            f[i][j] = gcd(f[i][j-1], f[i+(1<<(j-1))][j-1]);
        }
    }
}
int query(int l, int r)
{
    int k = mm[r-l+1];
    return gcd(f[l][k], f[r-(1<<k)+1][k]);
}
void pre()
{
    for(int i = 1; i <= n; i++) {
        int g = f[i][0];
        int L = i, tmp;
        while(L <= n) {
            int l = L, r = n;
            while(l <= r) {
                int mid = l+r>>1;
                if(query(i, mid) == g) {
                    tmp = mid;
                    l = mid+1;
                } else {
                    r = mid-1;
                }
            }
            mp[g] += (tmp-L+1);
            L = tmp+1;
            g = gcd(g, f[L][0]);
        }
    }
}
int main()
{
    int t, m, l, r;
    cin>>t;
    for(int casee = 1; casee <= t; casee++) {
        cin>>n;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            f[i][0] = a[i];
        }
        mp.clear();
        initrmq();
        pre();
        cin>>m;
        printf("Case #%d:\n", casee);
        for(int i = 0; i < m; i++) {
            scanf("%d%d", &l, &r);
            int ans = query(l, r);
            printf("%d %lld\n", ans, mp[ans]);
        }
    }
}
时间: 2024-10-09 12:07:12

hdu 5726 GCD 倍增+ 二分的相关文章

HDU 5726 GCD (RMQ + 二分)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5726 给你n个数,q个询问,每个询问问你有多少对l r的gcd(a[l] , ... , a[r]) 等于的gcd(a[l'] ,..., a[r']). 先用RMQ预处理gcd,dp[i][j] 表示从i开始2^j个数的gcd. 然后用map存取某个gcd所对应的l r的数量. 我们可以在询问前进行预处理,先枚举i,以i为左端点的gcd(a[i],..., a[r])的种类数不会超过log2(n)

HDU 5726 GCD 区间GCD=k的个数

GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2742    Accepted Submission(s): 980 Problem Description Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). There ar

HDU 5726 GCD(DP)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5726 [题目大意] 给出数列An,对于询问的区间[L,R],求出区间内数的GCD值,并且求出GCD值与其相等的区间总数 [题解] 首先,固定一个区间的右端点,利用GCD的递减性质,可以求出GCD相等的区间左端点的范围,将其范围的左右端点保存下来,同时,对于每个新产生的区间,以其GCD值为下标的MAP值+1,最后对于每个询问,在其右端点保存的范围中查找,获得其GCD值,同时在MAP中获取该GCD值

HDU 5726 GCD

传送门 GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem DescriptionGive you a sequence of $N(N≤100,000)$ integers : $a_1,\cdots,a_n(0<a_i≤1000,000,000)$. There are $Q(Q≤100,000)$ queries. For each query $l

hdu 4185 Oil Skimming(二分匹配)

Oil Skimming Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 883    Accepted Submission(s): 374 Problem Description Thanks to a certain "green" resources company, there is a new profitable

HDU 4417 划分树+二分

题意:有n个数,m个询问(l,r,k),问在区间[l,r] 有多少个数小于等于k. 划分树--查找区间第k大的数.... 利用划分树的性质,二分查找在区间[l,r]小于等于k的个数. 如果在区间第 i 大的数tmp>k,则往下找,如果tmp<k,往上找. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #incl

HDU 1695 GCD 欧拉函数+容斥原理+质因数分解

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695 题意:在[a,b]中的x,在[c,d]中的y,求x与y的最大公约数为k的组合有多少.(a=1, a <= b <= 100000, c=1, c <= d <= 100000, 0 <= k <= 100000) 思路:因为x与y的最大公约数为k,所以xx=x/k与yy=y/k一定互质.要从a/k和b/k之中选择互质的数,枚举1~b/k,当选择的yy小于等于a/k时,可以

HDU 1281 棋盘游戏(二分匹配 与 删边)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1281 根据题目描述,什么是重要点?在求出最大匹配后,进行枚举,依次删边,看最大匹配数会不会发生改变,改变的话,那么该点就是重要点. #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <

HDU 2119 Matrix 简单二分匹配

行做x集,列做y集,1就给该行该列连一条边,输出最大匹配边即可 #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> #include<set> using namespace std; #define N 105 int lef[N], pn;//lef[v]表示Y集的点v 当前连接的点 , pn为x点集的