hdu 5869 (求区间不同gcd的个数)

 

Different GCD Subarray Query

 

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 999    Accepted Submission(s): 381

 

 

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 new problem about GCD. Easy as it looks, Bob cannot figure it out himself. Now he turns to you for help, and here is the problem:
  
  Given an array a of N positive integers a1,a2,⋯aN−1,aN; a subarray of a is defined as a continuous interval between a1 and aN. In other words, ai,ai+1,⋯,aj−1,aj is a subarray of a, for 1≤ijN. For a query in the form (L,R), tell the number of different GCDs contributed by all subarrays of the interval [L,R].
  

 

 

Input

There are several tests, process till the end of input.
  
  For each test, the first line consists of two integers N and Q, denoting the length of the array and the number of queries, respectively. N positive integers are listed in the second line, followed by Q lines each containing two integers L,R for a query.

You can assume that
  
    1≤N,Q≤100000
    
   1≤ai≤1000000

 

 

Output

For each query, output the answer in one line.

 

 

Sample Input

 

5 3 1 3 4 6 9 3 5 2 5 1 5

 

 

Sample Output

 

6 6 6

 

 

Source

2016 ACM/ICPC Asia Regional Dalian Online

 

 

Recommend

wange2014   |   We have carefully selected several similar problems for you:  5900 5899 5897 5896 5895

 

 

 

题意:n个数,m个询问,每次询问一段区间的不同gcd的个数。

分析:因为给的n的范围是(1-100000),所以直接处理所有区间是很难的。所以我们可以先把询问保存起来,然后根据r排序。

这样我们就可以通过数状数组,维护一个以i为结尾的一个数组中有多少不同gcd,然后接下来重点是怎么更新了。

首先我们可以预处理出以每个子区间(以当前节点为结尾)的gcd,然后我们可以记录下在增加这个节点前,当前gcd值的所在点,然后更新,我们要尽可能的把相关的

记录位置右移(也就是成立gcd的子区间的左端点),这里就是简单的数状数组单点更新了。

 

代码:

 

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int h[maxn],sum[maxn*4];
int sign[maxn*10];
int ans[maxn];

vector <pair <int,pair <int,int> > > v[maxn];
struct node
{
    int l,r;
    int loc;
} s[maxn];

int lowbit(int x)
{
    return x & (-x);
}

void add(int x, int num,int N)
{
    while(x <= N)
    {
        sum[x] += num;
        x += lowbit(x);
    }
}

int query(int x)
{
    if(!x) return 0;
    return sum[x] + query(x - lowbit(x));
}

bool cmp(node as,node zx)
{
    if(as.r!=zx.r)
        return as.r<zx.r;
    else return as.l<zx.l;
}

void solve(int n,int m)
{
    int last=0;
    for(int i=1; i<=m; i++)
    {
        if(s[i].r<=last)
        {
            ans[s[i].loc]=query(s[i].r)-query(s[i].l-1);
        }
        else
        {
            for(int j=last+1; j<=s[i].r; j++)
            {
                for(int k=0; k<v[j].size(); k++)
                {
                    if(v[j][k].second.first)
                        add(v[j][k].second.first,-1,n);
                    add(v[j][k].second.second,1,n);
                }
            }
            last=s[i].r;
            i--;
        }
    }
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n, &m))
    {
        memset(sign,0,sizeof(sign));
        memset(sum,0,sizeof(sum));
        for(int i=1; i<=n; i++)//预处理部分
        {
            v[i].clear();
            scanf("%d",&h[i]);
            for(int j=0; j<v[i-1].size(); j++)
            {
                int x=__gcd(v[i-1][j].first,h[i]);
                v[i].push_back(make_pair(x,make_pair(sign[x],v[i-1][j].second.second)));
                sign[x]=v[i-1][j].second.second;
                for(int k=0; k<v[i].size()-1; k++)
                {
                    if(v[i][k].first==x)
                    {
                        v[i][v[i].size()-1].second.first=v[i][k].second.first;
                        v[i].erase(v[i].begin()+k);
                        k--;
                    }
                }
            }
            v[i].push_back(make_pair(h[i],make_pair(sign[h[i]],i)));
            sign[h[i]]=i;
            for(int k=0; k<v[i].size()-1; k++)//要不之前的相同gcd值点删除,不然极限状态下会爆内存
            {
                if(v[i][k].first==h[i])
                {
                    v[i][v[i].size()-1].second.first=v[i][k].second.first;
                    v[i].erase(v[i].begin()+k);
                    k--;
                }
            }
        }
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&s[i].l,&s[i].r);
            s[i].loc=i;
        }
        sort(s+1,s+1+m,cmp);
        solve(n,m);
        for(int i=1; i<=m; i++)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

 

 

 

 

 

 

好的,关于 HDU4992 所有原根的问题,我可以给出以下解答: 1. 首先,我们需要知道什么是原根。原根是指模 n 意义下,所有与 n 互质的数 a 的最小正整数 k 次幂对 n 取模的值覆盖了所有与 n 互质的数。简单来说,就是如果一个数 a 是模 n 意义下的原根,那么 a 的任何正整数次幂对 n 取模的值都不相同,且能覆盖所有与 n 互质的数。 2. 为了模 n 意义下的所有原根,我们需要先出与 n 互质的数的个数 phi(n)。phi(n) 可以使用欧拉函数出。 3. 然后,我们需要枚举模 n 意义下的所有数,判断它是否是原根。具体来说,对于每个 a,我们需要判断 a 的每个小于 phi(n) 的正整数次幂对 n 取模的值是否都不相同,且能覆盖所有与 n 互质的数。如果是,那么 a 就是模 n 意义下的原根。 4. 代码实现可以参考以下 Java 代码: ``` import java.util.*; public class Main { static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } static int phi(int n) { int res = n; for (int i = 2; i * i <= n; i++) { if (n % i == 0) { res = res / i * (i - 1); while (n % i == 0) { n /= i; } } } if (n > 1) { res = res / n * (n - 1); } return res; } static int pow(int a, int b, int mod) { int res = 1; while (b > 0) { if ((b & 1) != 0) { res = res * a % mod; } a = a * a % mod; b >>= 1; } return res; } static boolean check(int a, int n, int phi) { for (int i = 1, j = pow(a, i, n); i <= phi; i++, j = j * a % n) { if (j == 1) { return false; } } return true; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { int n = scanner.nextInt(); int phi = phi(n); List<Integer> ans = new ArrayList<>(); for (int i = 1; i < n; i++) { if (gcd(i, n) == 1 && check(i, n, phi)) { ans.add(i); } } Collections.sort(ans); for (int x : ans) { System.out.print(x + " "); } System.out.println(); } } } ``` 其中,gcd 函数用于最大公约数,phi 函数用于欧拉函数,pow 函数用于快速幂模,check 函数用于判断一个数是否是原根。在主函数中,我们依次读入每个 n,出 phi(n),然后枚举模 n 意义下的所有数,判断它是否是原根,将所有原根存入一个 List 中,最后排序输出即可。 希望我的回答能够帮到你,如果你有任何问题,欢迎随时提出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值