HDU - 6287 口算训练 二分+质因数分解

小Q非常喜欢数学,但是他的口算能力非常弱。因此他找到了小T,给了小T一个长度为 nn的正整数序列a1,a2,...,ana1,a2,...,an,要求小T抛出mm个问题以训练他的口算能力。

每个问题给出三个正整数l,r,dl,r,d,小Q需要通过口算快速判断al×al+1×...×ar1×aral×al+1×...×ar−1×ar是不是dd的倍数。

小Q迅速地回答了出来,但是小T并不知道正确答案是什么,请写一个程序帮助小T计算这些问题的正确答案。

Input第一行包含一个正整数T(1T10)T(1≤T≤10),表示测试数据的组数。

每组数据第一行包含两个正整数n,m(1n,m100000)n,m(1≤n,m≤100000),分别表示序列长度以及问题个数。

第二行包含nn个正整数a1,a2,...,an(1ai100000)a1,a2,...,an(1≤ai≤100000),表示序列中的每个数。

接下来mm行,每行三个正整数l,r,d(1lrn,1d100000)l,r,d(1≤l≤r≤n,1≤d≤100000),表示每个问题。
Output对于每个问题输出一行,若是倍数,输出Yes,否则输出No。Sample Input

1
5 4
6 4 7 2 5
1 2 24
1 3 18
2 5 17
3 5 35

Sample Output

Yes
No
No
Yes

思路参考链接:https://blog.csdn.net/wjmwsgj/article/details/80519183

对于一个数(k)是不是d的倍数这类问题,我们可以对这两个数分解质因数,之后看看k的质因数和d的质因数之间的关系,如果满足对于d的每一个质因数个数,在k中都有出现过,且k的出现次数要大于等于d的出现次数,这个就是满足的,举个例子,36 和 6 ,36的质因数有 2,2,3,3,6的质因数有 2,3,其中36中2出现的次数大于了6中2出现的次数,36中3出现的次数大于了6中3出现的次数,故36是6的倍数。

那么对于这道题,我们需要做的就是先将这n个数分解质因数,对于区间问题我们要怎么解决呢?我们把质因数出现的位置给存下来,比如说我们现在在第3个数,a[3] = 8 ,那么我们开一个vector w[2]里加入 3,3,3,3,(相当于w[2]里面记录的是2这个质因子是下标为2这个数的因子)之后,之后比如说我们现在要查询l,r的区间是不是d的倍数,我们将d分解质因数之后用二分去查询我们当前的区间里有没有这个值就好了。

刚开始用的质因子分解方法复杂度太大,导致TLE

 1 const int maxn=100005;
 2 vector<int>w[maxn];
 3 int maxx=0;
 4 bool isprim(int x)
 5 {
 6     for(int i=2; i<=sqrt(x); ++i)
 7     {
 8         if(x%i==0) return 0;
 9     }
10     return 1;
11 }
12 void digui(int x,int id)
13 {
14     int temp=sqrt(x);
15     if(isprim(x))
16         w[x].push_back(id),maxx=max(maxx,x);
17     else
18     {
19         for(int i=2; i<=temp; ++i) //这个for循环作用就是找一个x的因子
20         {
21             if(x%i==0)
22             {
23                 digui(i,id);
24                 digui(x/i,id);
25             }
26         }
27     }
28 }

TLE全部代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<math.h>
 6 #include<vector>
 7 using namespace std;
 8 typedef long long ll;
 9 const int maxn=100005;
10 vector<int>w[maxn];
11 int maxx=0;
12 bool isprim(int x)
13 {
14     for(int i=2; i<=sqrt(x); ++i)
15     {
16         if(x%i==0) return 0;
17     }
18     return 1;
19 }
20 void digui(int x,int id)
21 {
22     int temp=sqrt(x);
23     if(isprim(x))
24         w[x].push_back(id),maxx=max(maxx,x);
25     else
26     {
27         for(int i=2; i<=temp; ++i) //这个for循环作用就是找一个x的因子
28         {
29             if(x%i==0)
30             {
31                 digui(i,id);
32                 digui(x/i,id);
33             }
34         }
35     }
36 }
37 int main()
38 {
39     int t;
40     scanf("%d",&t);
41     while(t--)
42     {
43         maxx=0;
44         int n,m;
45         scanf("%d%d",&n,&m);
46         for(int i=0;i<maxn;i++)
47             w[i].clear();
48         for(int i=0;i<n;++i)
49         {
50             int x;
51             scanf("%d",&x);
52             digui(x,i);
53         }
54 //        for(int i=0;i<maxx;i++)
55 //            sort(w,w+w[i].size());
56         while(m--)
57         {
58             int flag=0,l,r,d;
59             scanf("%d%d%d",&l,&r,&d);
60             l--,r--;
61             for(int i=2;i<=sqrt(d);++i)
62             {
63                 int num=0;
64                 while(d%i==0)
65                     d/=i,num++;
66                 if(num)
67                 {
68                     int pos=upper_bound(w[i].begin(),w[i].end(),r)-lower_bound(w[i].begin() , w[i].end(),l);
69                     if(pos < num) //如果小于我们现在需要的,就说明不行。
70                     {
71                         flag=1;
72                         break;
73                     }
74                 }
75             }
76             if(d>1)  //同理
77             {
78                 int pos=upper_bound(w[d].begin(),w[d].end(),r)-lower_bound(w[d].begin() , w[d].end(),l);
79                 if(pos==0) flag=1;
80             }
81             if(flag)
82             {
83                 printf("No\n");
84             }
85             else
86             {
87                 printf("Yes\n");
88             }
89         }
90     }
91     return 0;
92 }
View Code

实际上不需要用isprim来判断某个数是不是素数

看下面一种方法:

 1 const int maxn=100005;
 2 vector<int>w[maxn];
 3 int maxx=0;
 4 void resolve(int x,int id)
 5 {
 6     for(int i = 2 ; i * i <= x ; i++) // 这里的i最大是313
 7     {
 8         while(x % i == 0)  //一直除的话就不会遇到i是合数的情况
 9         {
10             w[i].push_back(id);
11             x = x/i;
12         }
13     }
14     if(x>1)  // 这里有一个问题就是 我sqrt(maxn)里的最大的质数其实是313,那么我来了一个2 * 313的时候,我们找到的质因数只有2 ,所以当他没有到1的时候说明我们没有完
15     {
16         w[x].push_back(id); // 需要在搞一下
17     }
18 }

正确代码:

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<math.h>
  6 #include<vector>
  7 using namespace std;
  8 typedef long long ll;
  9 const int maxn=100005;
 10 vector<int>w[maxn];
 11 int maxx=0;
 12 void resolve(int x,int id)
 13 {
 14     for(int i = 2 ; i * i <= x ; i++) // 这里的i最大是313
 15     {
 16         while(x % i == 0)  //一直除的话就不会遇到i是合数的情况
 17         {
 18             w[i].push_back(id);
 19             x = x/i;
 20         }
 21     }
 22     if(x>1)  // 这里有一个问题就是 我sqrt(maxn)里的最大的质数其实是313,那么我来了一个2 * 313的时候,我们找到的质因数只有2 ,所以当他没有到1的时候说明我们没有完
 23     {
 24         w[x].push_back(id); // 需要在搞一下
 25     }
 26 }
 27 /*这一种质因数分解太耗时了*/
 28 //bool isprim(int x)
 29 //{
 30 //    for(int i=2; i<=sqrt(x); ++i)
 31 //    {
 32 //        if(x%i==0) return 0;
 33 //    }
 34 //    return 1;
 35 //}
 36 //void digui(int x,int id)
 37 //{
 38 //    int temp=sqrt(x);
 39 //    if(isprim(x))
 40 //        w[x].push_back(id),maxx=max(maxx,x);
 41 //    else
 42 //    {
 43 //        for(int i=2; i<=temp; ++i) //这个for循环作用就是找一个x的因子
 44 //        {
 45 //            if(x%i==0)
 46 //            {
 47 //                digui(i,id);
 48 //                digui(x/i,id);
 49 //            }
 50 //        }
 51 //    }
 52 //}
 53 int main()
 54 {
 55     int t;
 56     scanf("%d",&t);
 57     while(t--)
 58     {
 59         maxx=0;
 60         int n,m;
 61         scanf("%d%d",&n,&m);
 62         for(int i=0;i<maxn;i++)
 63             w[i].clear();
 64         for(int i=0;i<n;++i)
 65         {
 66             int x;
 67             scanf("%d",&x);
 68             //digui(x,i);
 69             resolve(x,i);
 70         }
 71 //        for(int i=0;i<maxx;i++)
 72 //            sort(w,w+w[i].size());
 73         while(m--)
 74         {
 75             int flag=0,l,r,d;
 76             scanf("%d%d%d",&l,&r,&d);
 77             l--,r--;
 78             for(int i=2;i<=sqrt(d);++i)
 79             {
 80                 int num=0;
 81                 while(d%i==0)
 82                     d/=i,num++;
 83                 if(num)
 84                 {
 85                     //upper_bound是找到大于r的
 86                     //low_bound是找到大于等于l的
 87                     int pos=upper_bound(w[i].begin(),w[i].end(),r)-lower_bound(w[i].begin() , w[i].end(),l);
 88                     if(pos < num) //如果小于我们现在需要的,就说明不行。
 89                     {
 90                         flag=1;
 91                         break;
 92                     }
 93                 }
 94             }
 95             if(d>1)  //同理
 96             {
 97                 int pos=upper_bound(w[d].begin(),w[d].end(),r)-lower_bound(w[d].begin() , w[d].end(),l);
 98                 if(pos==0) flag=1;
 99             }
100             if(flag)
101             {
102                 printf("No\n");
103             }
104             else
105             {
106                 printf("Yes\n");
107             }
108         }
109     }
110     return 0;
111 }
View Code

猜你喜欢

转载自www.cnblogs.com/kongbursi-2292702937/p/12712722.html