hdu 5869 区间gcd的求法及应用

题意:长度n的序列, m个询问区间[L, R], 问区间内的所有连续子段的不同GCD值有多少种.

题解:

  1.因为n个数的gcd等于前n-1个数的gcd值再于第n个数gcd一下的值,再加上如果固定终点,区间向前延伸越多gcd必定是非严格递减的,所以我们可以预处理出以每一个数为终点的所有的后缀的gcd,每次求出第i个数为终点后记录下所有的gcd值再与第i+1个数求gcd,这个很好做。

  2.将所有的查询按右区间从小到大排序,同时将第一步记录的值在树状数组中更新,然后用树状数组区间求和就好了,这一步在代码里面解释

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
#define PF(x) cout << "debug: " << x << " ";
#define EL cout << endl;
#define PC(x) puts(x);
typedef long long ll;
#define CLR(x, v) sizeof (x, v, sizeof(x))
using namespace std;
const int INF = 0x5f5f5f5f;
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
const double eps = acos(-1);
const double PI= atan(1.0)*4;
int n,q,a[maxn],tree[maxn],vis[2000000],ans[maxn];
vector<pair<int,int> >gd[maxn];
struct st{
    int l,r,id;
}qer[maxn];
int gcd(int a,int b){
    return b == 0?a:gcd(b,a%b);
}
bool cmp(st x,st y){
    return x.r < y.r;
}
void Add(int k,int num){
    while(k <= n){
        tree[k] += num;
        k += k&(-k);
    }
}
int Sum(int k){
    int sum = 0;
    while(k > 0){
        sum += tree[k];
        k -= k&(-k);
    }
    return sum;
}
int main(){
    freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&q)){
        for(int i = 1;i <= n;i++){
            scanf("%d",&a[i]);
            gd[i].clear();
        }
        for(int i = 1;i <= n;i++){
            int x = a[i],y = i;
            for(int j = 0;j <gd[i-1].size();j++){
                int t = gcd(gd[i-1][j].first,a[i]);
                if(t != x){
                    gd[i].push_back(make_pair(x,y));
                    x = t,y = gd[i-1][j].second;//x记录从y位置到当前处理的i位置的gcd值,y为相同gcd中最右边的位置,最右边这个是必须的
                }
            }
            gd[i].push_back(make_pair(x,y));
        }
       for(int i = 1;i <= q;i++){
            scanf("%d%d",&qer[i].l,&qer[i].r);
            qer[i].id = i;
       }
        sort(qer + 1,qer + 1 + q,cmp);
        memset(vis,0,sizeof(vis));
        memset(tree,0,sizeof(tree));
        int len = 1;
        for(int i = 1; i <= n;i++){
           // cout<<i<<":";
            for(int j = 0;j < gd[i].size();j++){
               // cout<<gd[i][j].first<<"->"<<gd[i][j].second<<" ";
                int c1 = gd[i][j].first,c2 = gd[i][j].second;
              //如果这个gcd值c1出现过则更新其位置,根据非严格递减,                             这样一定能更新为最靠近当前的i的位置
                if(vis[c1] > 0)
                    Add(vis[c1],-1);
                vis[c1] = c2;
                Add(c2,1);
            }
           // cout<<endl;
            while(qer[len].r == i){
                ans[qer[len].id] = Sum(qer[len].r) - Sum(qer[len].l-1);
                len++;
            }
        }
        for(int i = 1;i <= q;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}
            

可以参考博客http://blog.csdn.net/angon823/article/details/52503408

时间: 2024-10-27 07:26:29

hdu 5869 区间gcd的求法及应用的相关文章

hdu 5869 区间不同GCD个数(树状数组)

Different GCD Subarray Query Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 221    Accepted Submission(s): 58 Problem Description This is a simple problem. The teacher gives Bob a list of probl

HDU 5869 Different GCD Subarray Query 树状数组 + 一些数学背景

http://acm.hdu.edu.cn/showproblem.php?pid=5869 题意:给定一个数组,然后给出若干个询问,询问[L, R]中,有多少个子数组的gcd是不同的. 就是[L, R]中不同区间的gcd值,有多少个是不同的. 给个样例 3 37 7 71 21 33 3 数学背景: 一个数字和若N个数字不断GCD,其结果只有loga[i]种,为什么呢?因为可以把a[i]质因数分解,其数目最多是loga[i]个数字相乘.(最小的数字是2,那么loga[i]个2相乘也爆了a[i]

HDU 5869 Different GCD Subarray Query (GCD种类预处理+树状数组维护)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5869 问你l~r之间的连续序列的gcd种类. 首先固定右端点,预处理gcd不同尽量靠右的位置(此时gcd种类不超过loga[i]种). 预处理gcd如下代码,感觉真的有点巧妙... 1 for(int i = 1; i <= n; ++i) { 2 int x = a[i], y = i; 3 for(int j = 0; j < ans[i - 1].size(); ++j) { 4 int g

hdu 5869 Different GCD Subarray Query BIT+GCD 2016ICPC 大连网络赛

Different GCD Subarray Query Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 828    Accepted Submission(s): 300 Problem Description This is a simple problem. The teacher gives Bob a list of prob

HDU 5869 Different GCD Subarray Query 离线+树状数组

Different GCD Subarray Query Problem Description This is a simple problem. The teacher gives Bob a list of problems about GCD (Greatest Common Divisor). After studying some of them, Bob thinks that GCD is so interesting. One day, he comes up with a 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

dutacm.club 1094: 等差区间(RMQ区间最大、最小值,区间GCD)

1094: 等差区间 Time Limit:5000/3000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)Total Submissions:655   Accepted:54 [Submit][Status][Discuss] Description 已知一个长度为 n 的数组 a[1],a[2],-,a[n],我们进行 q 次询问,每次询问区间 a[l],a[l+1],-,a[r?1],a[r] ,数字从小到大

hdu 5700区间交(线段树)

区间交 Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 849    Accepted Submission(s): 377 Problem Description 小A有一个含有n个非负整数的数列与m个区间.每个区间可以表示为li,ri. 它想选择其中k个区间, 使得这些区间的交的那些位置所对应的数的和最大. 例如样例中,选择[2,5]

HDU 3911 区间合并求最大长度的问题

http://vjudge.net/problem/viewProblem.action?id=21557 题目大意: 每进行一次颜色改变都可以把一段区间内的黑石头变成白石头,白石头变成黑石头,最后问区间内黑石头连续的最大长度 这里我们可以用rev[]作为lazy标记,每次进行改变,rev[]^1 因为有黑白两种石头,我们求连续区间,需要维护黑,白两种石头的左侧最多,右侧最多和全部最多,所以我们这里可以用一个二维数组进行描述 每次做出改变,只要将黑白石头的值进行交换即可就方便了很多 对于最后访问